Ruby Classes
September 21, 2021 in ruby

A Ruby class definition is just regular code that runs. When you use the class keyword to create a class, you aren’t just dictating how objects will behave in the future. You are actually running code.

You can put any code in a class definition, and it returns the value of the last statement, just like methods and blocks do.

name = class Ruby
  puts "Hello world"
  creator = "Matz"
end

puts name

# Output
Hello world
Matz

Inside the class definition, the class itself takes the role of the self.

klass = class Ruby
  self
end

puts klass		# Ruby
puts klass.new		# <Ruby:0x00007fbd25854af0>

To access variables defined outside the class definition (i.e., flattening the scope), use class_eval. Inside, you can also define new methods dynamically on the class, using define_method.

ror = "Ruby on Rails"

class Ruby; end

Ruby.class_eval do
  puts ror

  define_method(:run) do
    puts "Hello, from the new method"
  end
end

Ruby.new.run

# Output
# Ruby on Rails
# Hello, from the new method

Instance variables of a class (class instance variables) are different from the instance variables of that class’s objects. If you come from Java or C#, it’s tempting to think that the class instance variables are just the static fields of the class. Instead, they’re just regular instance variables of an object of class Class.

The following example illustrates this. The class Ruby is an object of the class Class, whereas rb is an object of the class Ruby.

class Ruby
  # ror is a instance variable of class 'Ruby'
  @ror = "Ruby on Rails" 

  def self.rails
    puts @ror
  end

  def initialize
    # language is an instance variable of 'a Ruby object'
    @language = "Ruby" 
  end

  def execute
    puts @language
  end
end

rb = Ruby.new

# valid code
Ruby.rails
rb.execute

# invalid code
Ruby.execute
rb.rails

# Output
#
# Ruby on Rails
# Ruby
# undefined method `execute' for Ruby:Class
# undefined method `rails' for #<Ruby:0x00007ff1...

Singleton Methods

When you define a method on a class, e.g. Ruby.rails, it’s also known as a singleton method of a class.

Here’s the syntax to define singleton method on an object. Here, object can be an object reference, a class name, or self.

def object.method_name
  # method body
end

Singleton Classes

A singleton class is where an object’s singleton methods live, whether that object is a class name or an object reference. You can access the singleton class by calling singleton_class method on the object.

The superclass of the singleton class of an object is the object’s class.

The superclass of the singleton class of a class is the singleton class of the class’s superclass.

from: Metaprogramming Ruby 2

Callable Objects
September 20, 2021 in ruby

There are at least three places in Ruby where you can package code to be executed later. In a block, a proc/lambda, and a method.

Block

A block is a chunk of code you can associate with method invocations, almost as if they were parameters. You can use code blocks to implement callbacks, pass around chunks of code, and implement iterators.

Here are two different ways to define blocks. We use the first version with braces for single-line blocks and do/end for multi-line blocks.

# this is a code block
{ puts "hello" } 

# this is also a code block
do
  puts "hello"
  puts "hello world"
end

A block is typically associated with a call to a method, by putting the block at the end of the source line containing the method call. The method can then invoke the block using the yield keyword in Ruby.

def greet(name)
  puts "hey, #{name}"
  yield
end

greet("Matz") { puts "Thanks for creating Ruby" }

greet("David") do 
  puts "Thanks for creating Rails"
end

You can also pass arguments to the call to yield, and Ruby will pass them to the block.

def add(a, b)
  sum = a + b
  yield sum
end

add(3, 4) { |sum| puts "result: #{sum}" }

add(3, 4) do |sum|
  puts "result: #{sum}"
end

Proc

A Proc object encapsulates a block, allowing us to store the block in a local variable, pass it around, and execute it later.

There are four ways to convert a block into a Proc object.

  • Pass a block to a method whose last parameter starts with &.
def foo(p1, p2, &block)
  puts block.inspect
end

# #<Proc:0x00007fd9ff119848 main.rb:5>
foo(1, 3) { "a block" }

# nil
foo(3, 4)
  • Call Proc.new or Kernel#proc, associating it with a block.
block = Proc.new { |arg| puts "a block with argument: #{arg}" }
block_new = proc { |arg| puts "shorthand block with argument: #{arg}" }

#<Proc:0x00007fa532035ac8 main.rb:1>
puts block.inspect
  • Call the method Kernel#lambda and associate a block with it. Notice the appended (lambda) when you inspect the object.
block = lambda { |arg| puts "a block with argument: #{arg}" }

#<Proc:0x00007fa77f04d8b8 main.rb:1 (lambda)>
puts block.inspect
  • Using the -> syntax.
block_arrow = -> (arg) { puts arg }

# #<Proc:0x00007fb388830fc0 main.rb:7 (lambda)>
puts block_arrow.inspect

Once you have a Proc object, you can execute its code by invoking its methods call, yield, or [].

block = proc { |arg| puts "a block with argument: #{arg}" }

# a block with argument: 10
block.call 10

Lambda

The Proc objects created with the lambda syntax (3rd and 4th option above) are called lambdas. To check if a Proc object is a lambda, call the Proc#lambda? method on it.

block = proc { |arg| puts "a block with argument: #{arg}" }
puts block.lambda?		# false

block_lamb = lambda { |arg| puts "a block with argument: #{arg}" }
puts block_lamb.lambda?		# true

block_arrow = -> (arg) { puts "a block with argument: #{arg}" }
puts block_arrow.lambda?		# true

The lambdas differ from the Proc objects created any other way. We will see the differences in a future post.

Method

The Kernel#method returns a method itself as a Method object. You can later execute this method object by invoking the call method on it, similar to a Proc.

class Animal
  def greet
    puts "Hello World"
  end
end

bunny = Animal.new
greet_method = bunny.method(:greet)
greet_method.call   # Hello World

You can convert a Method to a Proc by calling Method#to_proc. You can also convert a block to a method by using define_method and associating the block with it.

An important difference between the lambdas and methods is that Ruby evaluates a lambda in the scope it’s defined it. In contrast, a Method is evaluated in the scope of its object.

from: Metaprogramming Ruby 2

Scope Flattening
September 18, 2021 in ruby

In C# and Java, variables from the outer scope are visible to the inner scope. In Ruby, scopes are sharply separated, and nested visibility is not allowed. As soon as the control enters a new scope, i.e. a class, module, or a method, the previous bindings are replaced by a new set of bindings.

The following example illustrates the above point. When the program control enters the class Runner, the variable num falls out of scope and is no longer visible inside the class or the method.

num = 1

class Runner
  puts "#{num} from class"

  def run
    puts "#{num} from method"
  end
end

Runner.new.run

# (NameError): undefined local variable or method `num' for Runner:Class

A workaround is to use Ruby’s powerful meta-programming features. In Ruby, you can define new classes and methods on the fly, using Class.new and define_method, respectively, and passing a block.

num = 1

Runner = Class.new do
  puts "#{num} from class"

  define_method :run do
    puts "#{num} from method"
  end
end

Runner.new.run

# Output
# ==============
# 1 from class
# 1 from method

Replacing the scope gates (class, method, etc.) with a method call allows one scope to see variables from the outside scope. This is also known as “flattening the scope”.

from: Metaprogramming Ruby 2

Rails Middleware
September 15, 2021 in rails

Here’s a list of middleware Rails uses in a development environment. You can view the middleware by running the bin/rails middleware command from your application directory.

Rack::MiniProfiler

Displays speed badge for every HTML page. Designed to work both in production and in development.

ActionDispatch::HostAuthorization

Guards from DNS rebinding attacks by explicitly permitting the hosts a request can be sent to

Rack::Sendfile

Intercepts responses whose body is being served from a file and replaces it with a server specific X-Sendfile header. The web server is then responsible for writing the file contents to the client.

This can dramatically reduce the amount of work required by the Ruby backend and takes advantage of the web server’s optimized file delivery code.

ActionDispatch::Static

Serves static files from disk, if available. If no file is found, it hands off to the main app. In Rails apps, this middleware is configured to serve assets from the public directory.

ActionDispatch::Executor

Wraps requests with a supplied Executor. The Rails Executor separates application code from framework code. Any time the framework invokes your code, it will be wrapped by the executor.

The Executor consists of two callbacks: to_run and to_complete. The Run callback is called before the application code, and the Complete callback is called after.

ActiveSupport::Cache::Strategy::LocalCache::Middleware

Flushes memory based store used internally by Rails.cache

Rack::Runtime

Sets an “X-Runtime” response header, indicating the response time of the request, in seconds. You can put it right before the application to see the processing time, or before all the other middlewares to include time for them, too.

Rack::MethodOverride

HTML forms only support the GET and POST request. This middleware lets you override based on _method parameter, allowing you to use PUT or DELETE.

Makes a unique request id available to the action_dispatch.request_id env variable and sends the same id to the client via the X-Request-Id header.

ActionDispatch::RequestId

The unique request id is either based on the X-Request-Id header in the request, which would typically be generated by a firewall, load balancer, or the web server, or, if this header is not available, a random uuid.

The unique request id can be used to trace a request end-to-end and would typically end up being part of log files from multiple pieces of the stack.

ActionDispatch::RemoteIp

Calculates the IP address of the remote client that is making the request. Read more at: https://blog.gingerlime.com/2012/rails-ip-spoofing-vulnerabilities-and-protection/

Sprockets::Rails::QuietAssets

Suppresses logger output for asset requests.

Rails::Rack::Logger

Sets log tags, logs the request, calls the app, and flushes the logs.

ActionDispatch::ShowExceptions

This middleware rescues any exception returned by the application and calls an exceptions app that will wrap it in a format suitable for the the end-user.

WebConsole::Middleware

Sets up an interactive Ruby session in your browser.

ActionDispatch::DebugExceptions

Logs exceptions and shows a debugging page in case the request is local.

ActionDispatch::ActionableExceptions

Takes care of invoking actions from error page. Dispatches action to ActionableError and redirects back when action block has successfully run.

Source: https://www.bigbinary.com/blog/rails-6-adds-active-support-actionable-error

ActionDispatch::Reloader

Ensures any arriving HTTP request is served with a freshly-loaded copy of the application if there are any new code changes.

ActionDispatch::Callbacks

Provides callbacks to be executed before and after dispatching the request.

ActiveRecord::Migration::CheckPending

Verifies that all migrations have been run before loading a web page if config.active_record.migration_error is set to :page_load

ActionDispatch::Cookies

It reads and writes data to cookies through ActionController#cookies. When reading cookie data, the data is read from the HTTP request header, Cookie. When writing cookie data, the data is sent out in the HTTP response header, Set-Cookie.

ActionDispatch::Session::CookieStore

Stores the session in a cookie so it persists between requests. This cookie-based session store is the Rails default and it is dramatically faster than the alternatives.

ActionDispatch::Flash

Provides a way to pass temporary primitive-types (String, Array, Hash) between actions.

Anything you place in the flash will be exposed to the very next action and then cleared out. This is a great way of doing notices and alerts.

ActionDispatch::ContentSecurityPolicy::Middleware

Helps setting up the content-security-policy for your app, to guard against Cross-Site-Scripting attacks.

ActionDispatch::PermissionsPolicy::Middleware

Helps setting up the HTTP Permissions policy for defining a mechanism to allow and deny the use of browser permissions in its own context.

Rack::Head

Returns an empty body for all HEAD requests, leaving all other requests unchanged.

The HTTP HEAD method requests the headers that would be returned for a GET request with same URL. For example, if a URL might produce a large download, a HEAD request could read its Content-Length header to check the filesize without actually downloading the file.

Rack::ConditionalGet

Enables conditional GET using If-None-Match and If-Modified-Since. If response is the same as last request, it won’t send the last data again.

Rack::ETag

Automatically sets the ETag header on all String bodies.

Rack::TempfileReaper

Tracks and cleans the temporary files created throughout a request

Blog::Application.routes

Runs our application

Why, What, and How of DNS
September 7, 2021 in why-what-how

Programs can find web pages, mailboxes, and other resources using the IP addresses of their servers. An IP address looks like 128.111.24.11. Although these plain IP addresses are convenient for computers, they pose quite a few drawbacks for humans.

  1. They are difficult for people to remember and match against resources.
  2. If a resource changes the server, e.g. a web page moves from one web server to another with a different IP, all users need to update the stored IP address.

To address the first problem, we use a high-level, readable name to decouple the server name from the server IP address. This allows us to use a friendly, consistent name for a server, regardless of its IP address.

For example, we can refer to the popular question-answer website StackOverflow using a human-readable address https://stackoverflow.com instead of its IP address.

Stack Overflow

However, this solution creates another challenge: how do we convert a human-friendly name to a computer-friendly IP address? It also doesn’t answer the second problem where a resource changes the server. How do we keep the names up-to-date?

The solution for all the above problems is to use a system that maps individual IP addresses to human-friendly names and keeps them up-to-date. Any application program that needs to locate the physical address of a resource has to query this system to get its IP address.

A simple system would just be a centralized text file listing the computer names and their IP addresses. An administrator updates the file whenever a computer changes the IP address. All clients download this file every day to have the most recent information available to them.

Host File

For a small network, this approach works well. However, with millions of servers connected to the Internet, it fails. First, the size of the file becomes too large and impossible to maintain. Second, the names would constantly conflict unless the file was centrally managed. This is practically impossible on the Internet, due to load and latency.

To solve these concerns, DNS (Domain Name System) was invented in 1983 by Paul Mockapetris. It has been a core part of the Internet ever since.

What is DNS?

DNS is a hierarchical, domain-based naming scheme with a distributed database for implementing this naming scheme. The Internet uses it to map host names to IP addresses, but it can be used for other purposes. RFC 1034 introduces the DNS, and RFC 1035 describes it in detail, including its implementation and specification.

DNS manages the host names and IP addresses similarly to how the postal system manages people’s names with their home addresses. Multiple people can have the same name. Hence the postal office manages names by requiring letters to specify the country, state, city, street, and name. This is an example of a hierarchical addressing system.

Conceptually, the Internet is divided into more than 250 top-level domains, which are further partitioned into subdomains, and so on. Each domain covers many hosts. The following tree diagram represents this structure. A leaf domain may contain a single host, or it may represent an organization and contain thousands of hosts.

Hierarchical Addressing

The top-level domains are divided into two types: generic and countries. The generic ones include the original ones from the 1980s, e.g. .com, .org, etc. The country domains include one for each country. The top-level domains are run by registrars appointed by ICANN (Internet Corporation for Assigned Names and Numbers).

DNS Records

Every domain can have a set of resource records associated with it, forming the DNS database. The most common resource record is just its IP address. A resource record consists of the following five elements.

  1. Domain_name: The domain to which this record applies. Many records can exist for a single domain.
  2. Time_to_live (seconds): Indicates how long a client should use/cache this record. A large number indicates a stable domain that doesn’t change often. A low value indicates a volatile domain. A very common value is 86400, which is the number of seconds in a day.
  3. Class: Set to IN for Internet records. Rarely used for anything else.
  4. Type: Tells what kind of record this is, e.g. A, AAAA, NS, CNAME, TXT, etc.
  5. Value: Value of the record. Depending on the record type, it can be a domain name, a number, or an ASCII string.

Here are the most important types of DNS records.

  • A (Address): Holds a 32-bit IPv4 address for a host.
  • AAAA: Holds a 128-bit IPv6 address for a host.
  • MX: Name of the host prepared to accept email for this domain. Not every machine can accept email.
  • NS: Specifies a name server for the domain. A name server has a copy of the database for this domain. The client uses the name server to look up names.
  • CNAME: Used to create aliases.
  • TXT: Allows domains to identify themselves in arbitrary ways.

Name Servers

A name server is a computer containing the DNS database. In theory, a single name server could contain the entire DNS database, responding to all queries. However, similar to the text file we saw earlier, it won’t scale. If it ever goes down, the whole Internet would go down.

To add resiliency and scalability, the DNS namespace is divided into various zones. Each zone is associated with one or more name servers, which hold the database for that zone.

Typically, there will be one primary name server, and one or more secondary name servers for backup and reliability. The primary name server gets the information from a file on its disk, and the secondary ones get their information from the primary server.

Name Resolution

The process of looking up a domain name and fetching its address is called name resolution. When a client has a query about a domain name, it checks the local name server. If the domain falls under the jurisdiction of the local name server, it returns the authoritative resource records, which are always correct and up-to-date.

If the domain is remote and there’s no cached information in the local name server, the name server begins a remote query. It first asks one of the root name servers, containing the information about each top-level domain.

Note: To contact the root server, the local name server must know about the root server. This information is typically present in a sysconfig file on the name server. It’s loaded onto the DNS cache when the server starts. It’s simply a list of A records for the root.

There are 13 root DNS servers, named a.root-servers.net through m.root-servers-.net, hosted on powerful and heavily replicated computers, present in multiple geographical locations.

Domains

When the local name server contacts one of the root servers, it returns the address of the name server for the top-level domain name, e.g. .edu, .com, etc. The local server then continues its search by asking the name server of the top-level domain, until it finds the address of the domain it’s looking for. The following diagram illustrates this process:

Name Resolution

DNS uses the UDP protocol for queries and responses. If no message arrives within a short time, the DNS client repeats the query, trying another server for the domain after a small number of retries. This process ensures that the client gets a response even if a server is down.

Caching

All the intermediate results are cached on the respective servers. Hence, if the same query comes again, that server can respond directly without consulting another name server.

Though caching greatly reduces the number of queries, improving performance, it doesn’t always return the most up-to-date results, as the entry might have been updated in the authoritative name server. For this reason, cache entries don’t live too long. The exact time after which they should expire is specified in the Time_to_live field in each resource record. That tells the intermediate name servers how long to cache records.

Conclusion

DNS provides the mapping between human-readable domain names and the IP addresses of machines. It is a large and complex distributed system comprising millions of name servers working together. It provides replication and caching for high performance and reliability. It’s highly robust and reliable, and forms an essential part of the Internet infrastructure.

Caching in Rails
August 21, 2021 in rails

These are the notes I took while reading the Rails caching guide.

Caching is one of the most effective ways to boost an application’s performance. It stores content generated during the request-response cycle to reuse it when responding to similar requests.

Rails supports caching out of the box, and caching is enabled only in production. However, you can turn it on/off by running rails dev:cache command. You can access the cache by calling Rails.cache.

Rails provides three types of caching techniques: page, action, and fragment caching. Fragment caching is the default.

  1. Page Caching
    • Cache the generated page, so the web server (Apache or Nginx) can serve the request directly, without involving the entire Rails stack.
    • Though it is fast, it’s unsuitable for pages that need authentication.
    • You also have to implement cache expiration.
  2. Action Caching
    • Similar to page caching, except the incoming request hits the Rails stack.
    • This allows Rails to run the before filters like authentication before serving the cached response.
  3. Fragment Caching
    • Enabled by default, fragment caching is suitable for dynamic web applications, where different parts of the page need to be cached and expired separately.
    • It wraps a fragment of view logic in a cache block, to be served out of the cache store for future requests.
    • Consider the following code, which caches each product on a page. For the first request, Rails creates a new cache entry which is used for subsequent requests. The cache expires when the view fragment changes and/or the model is updated.
<% @products.each do |product| %>
	<% cache product do %>
  	<%= render product %>
  <% end %>
<% end %>

Russian Doll Caching

You can nest cached fragments inside other cached fragments. If a single entry is updated inside a cached collection, Rails reuses all the other fragments when regenerating the outer fragment.

SQL Query Caching

Allows you to cache the result set of a SQL query. If Rails sees the same query, it will use the cached result set, instead of executing the query against the database again.

Rails creates query caches at the start of an action and destroys at the end of that specific action. Hence, the query caches are persisted only for the duration of the action.

Cache Stores

You can set up your application’s default cache store by setting the config.cache_store configuration option.

config.cache_store = :memory_store

Rails provides the following cache stores:

  1. MemoryStore: keeps entries in memory in the same Ruby process.
  2. FileStore: uses file system to store the entries. You must specify the path to the directory where the cache files will be stored, when initializing the cache.
  3. MemCacheStore: uses Memcached as a cache.
  4. RedisCacheStore: uses Redis as a cache.

These were the basics of caching in Rails. I will add new posts as I learn more.

Exploring Rake
August 16, 2021 in ruby, rake

Rake is a task runner written in Ruby. It performs repetitive tasks, such as

  1. Making database backup
  2. Running tests
  3. Watch for file changes and compile the changed files

In the words of late Jim Weirich, the creator of rake,

The essence of rake is to take a task and break it down into its component pieces to specify what needs to be done to do the bigger task you are trying to do.

Usage

Put the following code in a Rakefile.

desc "Compile Code"
task :compile do
  puts "Compiling the code."
end

Run the task by running the following command

> rake compile

The description is listed when you run rake --task to see all available tasks.

> rake --task

rake compile         # Compile code

Dependent Tasks

task :interpret do
  puts "Interpreting the code"
end

task compile: "interpret" do
  puts "Compiling the code"
end
> rake compile
Interpreting the code
Compiling the code

To run multiple dependent tasks, provide their names as an array of symbols.

task compile: %i(clean sanitize interpret) do
  puts "Compiling the code"
end

Providing Defaults

Use the default task to specify all tasks to run when you run the rake command, without any arguments.

task default: %i[clean sanitize]

desc "Cleaning the code"
task :clean do
  puts "Cleaning the code"
end

desc "Sanitizing the code"
task :sanitize do
  puts "Sanitizing the code"
end
 > rake
 Cleaning the code
 Sanitizing the code

Scope

You can scope tasks with similar names under different namespaces.

task :backup do
  puts "Database backup"
end

namespace :file do
  task :backup do
    puts "File backup"
  end
end
rake backup
> Database backup

rake file:backup
> File backup

Access Environment Variables

task :show_state do
  puts "State = #{ENV["STATE"]}"
end
> export STATE=paused

> rake show_state
State = paused

Rake also allows you to pass the environment variable for the particular command. It’s only valid for that specific command, though.

> rake show_state STATE=running
State = running

> rake show_state          
State = paused

What’s Possible?

First, it’s all Ruby code. You can do anything in rake that you can do in Ruby. There’s no limit.

task :sum do
  result = 0
  (1..10).each do |n|
    result += n
  end
  puts result
end

However, rake is perfect for file manipulation. It includes the FileUtils standard library. So you can use all standard file manipulation commands.

desc "Taking a backup of the Rakefile"
task :backup do
  mkdir_p "data/backup"
  cp "Rakefile", "data/backup/Rakefile"
end

You can also run shell commands. This is especially handy when you want to run git add, git commit, and git push commands.

desc "git add, commit, and push"
task :track do
  sh "git add ."
  sh "git commit -m '#{ENV['m']}'"
  sh "git push origin main"
end

Run the command as follows:

>  rake track m="initialize project"

Finally, rake can run Ruby scripts, too.

task :run do
  ruby "greet.rb"
end

Summary

Rake is a task runner Jim Weirich wrote in Ruby. It allows you to specify dependent tasks to run them in order as well as automate repetitive tasks. You can leverage the power of shell and Ruby, which makes it very powerful. It’s a tool that you should know, even if you are not a Ruby developer.

Dynamic Instance Variables in Ruby
August 14, 2021 in ruby

Ruby continues to surprise me.

While reading Metaprogramming Ruby this evening, I learned that there is no connection between an object’s class and its instance variables in Ruby, unlike C#. The interpreter creates instance variables on the fly, when you assign them a value.

For example, a Task object won’t have the state instance variable until I call the run method on it.

class Task
  def run
    @state = :running
  end
  
  def stop
    @finished = true
  end
end

> compile = Task.new
> compile.instance_variables
=> []

> compile.run
=> :running

> compile.instance_variables
=> [:@state]

It’s possible to have objects of the same class with different instance variables.

For example, if I create another Task object and call the stop method, it won’t have a @state instance variable.

> interpret = Task.new
> interpret.instance_variables
=> []

> interpret.stop
=> true

> interpret.instance_variables
=> [:@finished]

Very interesting.

How to Check if a Variable is Defined?
August 13, 2021 in ruby

The defined? expression allows you to test if a variable is defined or not. If it isn’t defined, it returns nil. Otherwise, it returns a string that provides information about the variable.

> defined? a
=> nil

> a = 10
=> 10
> defined? a
=> "local-variable"

> defined? nil
=> "nil"

> defined? Array
=> "constant"
> defined? String
=> "constant"

* class Person
> end
> defined? Person
=> "constant"

> defined? 10
=> "expression"

A variable set to nil is also initialized.

> b = nil
=> nil

> defined? b
=> "local-variable"
Picking Random Value from an Array
August 11, 2021 in ruby

Often you want to pick a random value from an array of variable length. Typically, you would first find a random integer between zero and length of the array, and then use that as an index to pick the value. For example,

> languages = %w(ruby c-sharp java python cpp golang javascript)
=> ["ruby", "c-sharp", "java", "python", "cpp", "golang", "javascript"]

> languages[rand(languages.length)]
=> "c-sharp"

> languages[rand(languages.length)]
=> "javascript"

> languages[rand(languages.length)]
=> "java"

Is there an idiomatic way to do this in Ruby? Something that abstracts how we are generating a random number and using it as an index?

It turns out, there is.

Just use Array#sample. When no arguments are given, sample returns a random element from the array.

> languages.sample
=> "java"

> languages.sample
=> "c-sharp"

> languages.sample
=> "golang"

It returns nil if the array is empty.

> [].sample
=> nil

If you pass a number n, it returns a new array that contains n random elements from the original array.

> languages.sample(3)
=> ["c-sharp", "ruby", "javascript"]

> languages.sample(1)
=> ["javascript"]

> languages.sample(4)
=> ["javascript", "java", "golang", "python"]

If n is larger than the length of the array, sample returns a new array that contains all elements from the original array in shuffled order. No duplicates are introduced.

> languages.sample(10)
=> ["golang", "javascript", "ruby", "python", "cpp", "java", "c-sharp"]

Ruby makes me happy.