30
loading...
This website collects cookies to deliver better user experience
Note: This is an updated version of a previous unfinished Medium series of mine you can find here.
if
, unless
, and other branch type methods.if
statements look pretty well the same as you’d expect, except for the lack of parens:class Document
attr_accessor :writeable
attr_reader :title. :author, :content
# Much of the class omitted...
def title=(new_title)
if @writeable
@title = new_title
end
end
# Similar author= and content= methods omitted...
end
def title=(new_title)
if !@read_ony
@title = new_title
end
end
*not*
with *!*
as english operators aren’t often used. Granted I abuse this, but again, grain of salt)if ! == unless
while ! == until
unless
, it’s typically considered bad form to use an else
branch in the Ruby Style Guide:unless condition
# ...
else
# ...
end
else
is essentially the inverse of an unless
condition.unless @read_only
@title = new_title
end
@title = new_title unless @read_only
while
and until
as welldocument.print_next_page until document.printed?
data.each do |datum|
# ...
end if condition
for font in fonts
puts font
end
each
is preferred, especially once you get into Enumerable methods which build off of it:fonts.each do |font|
puts font
end
to_proc
coercion:fonts.each(&:register)
# ...is the same as:
fonts.each { |font| font.register }
for
loop in Ruby except that it feels more like another language.case title
when 'War And Peace'
puts 'Tolstoy'
when 'Romeo And Juliet'
puts 'Shakespeare'
else
puts "Don't know"
end
author =
case title
when 'War And Peace'
'Tolstoy'
when 'Romeo And Juliet'
'Shakespeare'
else
"Don't know"
end
author = case title
when 'War And Peace'
'Tolstoy'
when 'Romeo And Juliet'
'Shakespeare'
else
"Don't know"
end
author_name
? Now I have to indent every other branch in the statement to match it:author_name = case title
when 'War And Peace'
'Tolstoy'
when 'Romeo And Juliet'
'Shakespeare'
else
"Don't know"
end
author =
case title
when 'War And Peace'
'Tolstoy'
when 'Romeo And Juliet'
'Shakespeare'
else
"Don't know"
end
===
behind the scenes, and that’s precisely why I like them so much. If you haven't yet I would encourage reading an article on ===
.OR
of sorts:type_name =
case 1
when Integer, Float
"Number!"
when String
"String!"
else
"Dunno, too lazy"
end
===
too. I’m telling you, it’s Ruby’s best magic, especially once you figure out you can implement your own.case
statements:Point = Struct.new(:x, :y)
case Point[1, 2]
in Point[0.., 0..] then :positive
in Point[..0, ..0] then :negative
else :unsure
end
# => :positive
big_point = Point[10, 10]
case big_point
in Point[x: 10.. => x, y: 10.. => y] then Point[x + 1, y + 1]
else big_point
end
# > #<struct Point x=11, y=11>
0
is truthy in Ruby. That’s still the case, much to the annoyance of other language programmers. If it’s not false
or nil
it’s truthy.puts 'Sorry Dennis Ritchie, but 0 is true!' if 0
"false"
isn’t false. When it was said earlier that everything is truthy except nil
and false
that includes things which “look” like them. It’s certainly given lots of extra fun to Rails programmers with stringy booleans. Well, nightmares.puts 'Sorry but "false" is not false' if 'false'
if flag == true
# do something
end
defined?
is used as an example of where this can go wrong:doc = Document.new('A Question', 'Shakespeare', 'To be...')
flag = defined?(doc)
defined?
is an odd one, it returns a string for what type the variable is, but not as in data types:defined? a
# => nil
a = 5
# => 5
defined? a
# => "local-variable"
if defined?(a)
# ...
end
nil
:# Broken in a subtle way...
while next_object = get_next_object
# Do something with the object
end
false
and nil
are both falsy? If you’re expecting nil
explicitly, you should say so to prevent Ruby from breaking out of that loop early:until (next_object = get_next_object).nil?
# Do something with the object
end
||=
, but that’s mentioned in the next section so we’ll defer until then.if
block is from Ruby’s X509 certificate validation (with some cleaning):ret =
if @not_before && @not_before > time
[false, :expired, "not valid before '#{@not_before}"]
elsif @not_after && @not_after < time
[false, :expired, "not valid after '#{@not_after}'"]
elsif issuer_cert && !verify(issuer_cert.public_key)
[false, :issuer, "#{issuer_cert.subject} is not an issuer"]
else
[true, :ok, 'Valid certificate']
end
&.
), which lets us do this (assuming verify
deals well with nil
):ret =
if @not_before&.> time
[false, :expired, "not valid before '#{@not_before}"]
elsif @not_after&.< time
[false, :expired, "not valid after '#{@not_after}'"]
elsif !verify(issuer_cert&.public_key)
[false, :issuer, "#{issuer_cert.subject} is not an issuer"]
else
[true, :ok, 'Valid certificate']
end
Note that I’m not using the instance variable interpolation syntax, "#@var"
, as it’s exceptionally rare to see out in the wild. It's only real benefit is being a character shorter while making code a bit more confusing for newer programmers. When possible stick with common over clever.
file = all ? 'specs' : 'latest_specs'
defined?(a)
. Visual cues go a long way for understandability some times.if
statement instead:file =
all ?
'specs' :
'latest_specs'
@first_name = '' unless @first_name
@first_name ||= ''
@first_name = @first_name || ''
false
and nil
. Is an empty string one of those? No? Then it’s still truthy, be very careful to remember this.+=
from the example:count += 1
+
or ||
it’d override the +=
and ||=
respectively to potentially do very bad things.if
and friends has not changed much, and the warnings are still very relevant. Take heed as there’s some advice in programming which ages quite well.