| ruby |
The Paradox of Choice – Why More Is Less - is it applicable to programming languages?
I've been using simple (in terms of syntax) languages (Python, Go) for some time and have an understanding of why such fancy, flexible language as Ruby has sort of "irritation" from some software engineers.
Please note, that I am talking about language only, avoiding ecosystem, Rails, and so on.
Ruby is too flexible. It has too many ways of doing the same things using different methods. I want to concentrate on simple code, which I can read and understand quickly. Yes, I want to see elegant code as well, but today it's more important for me to follow a "less is more" approach, with few possible variations.
From my perspective, Ruby aligns more closely with Perl than with Python. I don't understand why they haven't deprecated some legacy in Ruby. It would make the language cleaner and remove ambiguity.
On the other hand, Ruby on Rails follows the "Less is More" principle, exemplified by its Convention over Configuration approach. This principle was a key factor in the framework's early success.
I decided to collect basic samples of doing the same/similar things in Ruby, ignoring meta-programming. It's not exhaustive list of "questionable" features. I am too lazy to write really long post, but it should give you of a feeling of what I mean.
require
: Loads a library or file once. Commonly used for loading gems or external libraries.require_relative
: Similar torequire
, but resolves file paths relative to the file making the call.load
: Loads and re-evaluates the code in a file every time it’s called.autoload
: Registers the given file to be loaded when the given constant is first referenced.
include
: Mixes module methods as instance methods into a class.extend
: Adds module methods as class methods or singleton methods.prepend
: Similar to include, but methods are added to the top of the method lookup chain, overriding existing methods.
array = [1, 2, 3, 4]
To create an array of strings without quotes and commas:
array = %w[apple banana cherry]
# => ["apple", "banana", "cherry"]
For strings with interpolation or escape sequences, use %W
:
name = "John"
array = %W[hello #{name} world]
# => ["hello", "John", "world"]
To create an array of symbols:
array = %i[one two three]
# => [:one, :two, :three]
The Array() method can convert other types to arrays.
array = Array(1..5)
# => [1, 2, 3, 4, 5]
Ruby provides an alternative to single and double-quote delimiters, which comes in handy sometimes when the string you want to quote contains the delimiter you need.
You can use various delimiters with %Q
. The most common are [ ]
, { }
, ( )
, | |
, or / /
. The opening and closing delimiters must match.
%q/general single-quoted string/ # => general single-quoted string
%Q!general double-quoted string! # => general double-quoted string
%Q{Seconds/day: #{24*60*60}} # => Seconds/day: 86400
%!general double-quoted string! # => general double-quoted string
%{Seconds/day: #{24*60*60}} # => Seconds/day: 86400
A heredoc allows you to build a multi-line string.
string = <<END_OF_STRING
The body of the string is the input lines up to
one starting with the same text that followed the '<<'
END_OF_STRING
string = <<-END_OF_STRING
The body of the string is the input lines up to
one starting with the same text that followed the '<<'
END_OF_STRING
And if you put a tilde after the << characters you can indent the text.
def a_long_string
<<~END_OF_STRING
Faster than a speeding bullet, more powerful than
a locomotive, able to leap tall buildings in a single
bound—look, up there in the sky, it's a bird, it's a
plane, it's Superman!
END_OF_STRING
end
puts a_long_string
You can also have multiple here documents on a single line
print <<-STRING1, <<-STRING2
Concat
STRING1
enate
STRING2
# produces:
# Concat
# enate
{ :one => "eins", :two => "zwei", :three => "drei" }
Using this syntax we tell Ruby that we want the keys to be symbols.
{ one: "eins", two: "zwei", three: "drei" }
Executes code if the condition is false
.
unless x >= 0
puts "x is negative"
else
puts "x is non-negative"
end
puts "x is negative" unless x >= 0
i = 1
loop do
puts "Message number #{i}"
i = i + 1
if i == 6
break
end
end
i = 1
while i <= 5 do
puts "Message number #{i}"
i = i + 1
end
for i in 1..5 do
puts "Message number #{i}"
end
i = 1
until i == 6 do
puts "Message number #{i}"
i = i + 1
end
print "Hello\n" while false
begin
print "Goodbye\n"
end while false
# produces:
# Goodbye
arr = [1, 2, 3]
arr.length # => 3
arr.size # => 3
arr = [1, 2, 3]
arr.map { |x| x * 2 } # => [2, 4, 6]
arr.collect { |x| x * 2 } # => [2, 4, 6]
arr = [1, 2, 3]
arr.reduce(:+) # => 6
arr.inject(:+) # => 6
arr = [1, 2, 3, 4]
arr.find { |x| x > 2 } # => 3
arr.detect { |x| x > 2 } # => 3
arr = [1, 2, 3, 4]
arr.select { |x| x.even? } # => [2, 4]
arr.find_all { |x| x.even? } # => [2, 4]
hash = { a: 1, b: 2 }
hash.key?(:a) # => true
hash.has_key?(:a) # => true
arr = [1, 2, 3, 4]
arr.reject { |x| x.even? } # => [1, 3]
arr.delete_if { |x| x.even? } # => [1, 3]
File.exists?("file.txt") # => true or false
File.exist?("file.txt") # => true or false
size
: Returns the size of the file in bytes.size?
: Returns the size if the file is non-empty; otherwise, returns nil.
File.size("file.txt") # => 1024
File.size?("file.txt") # => 1024 or nil
There are more similar cases, but you've got what I mean 😉, right?