Ruby
Language
#!/usr/bin/env ruby if $PROGRAM_NAME == __FILE__ puts "Hello, World!" end
- Author: Yukihiro Matsumoto (matz)
- 1995-
- Written in C
- Author wanted an OO language. Didn't like Python, because it wasn't true OO.
- challenges
- Typing: duck, dynamic, strong
- Everything is an object (including primitive data types)
- not variables, they hold references to an object
- use #dup when need a copy
- use #freeze to not admit modifications (numbers and symbols are by default)
- ergo, everything is a reference
- arguments are passed as references to functions
- not variables, they hold references to an object
- Optional () on function calls
- indentation isn't significant
- most lines that look like statements in Ruby are actually expression that return a value
- #tap
- useful for debugging chain of method calls
- takes a block, pass the receiver, and then return the orignal receiver of the method
- a "no-op", execept that invokes a block
- restricted to pure 7-bit ascii for operators, for compatibility across all different source file encodings
- when we call a method without putting a receiver object before it it refers to the object named "self" aka "puts == self.puts"
- lib/<gemname>/<class>.rb
- test/<gemname>/all_tests.rb # require_relative other .rbs
- test/<gemname>/<class>_test.rb
variables
- the "nouns" of Ruby
global variables
- https://www.fastruby.io/blog/exploring-global-constants-and-variables.html
- https://rubyapi.org/o/english
description | ||
---|---|---|
__FILE__ | has the name of the file being run | |
$stdin | standard input | |
$stdout | standard output | |
$stderr | standard error | |
$ARGV | $* | array with command line arguments |
$ARGF | $< | the content of all the files whose names were passed |
$DEFAULT_OUTPUT | $> | |
$ENV | Hash that contains environment variables | |
$ERROR_INFO | $! | |
$ERROR_POSITION | $@ | |
$FIELD_SEPARATOR | $; | FS |
$OUTPUT_FIELD_SEPARATOR | $, | OFS |
$INPUT_RECORD_SEPARATOR | $/ | RS |
$OUTPUT_RECORD_SEPARATOR | $\ | ORS |
$INPUT_LINE_NUMBER | $. | NR |
$LAST_READ_LINE | $_ | |
$PROCESS_ID | $$ | PID |
$CHILD_STATUS | $? | |
$MATCH | $& | regex op, the match (all between //) |
$PREMATCH | $` | regex op, to the left of last match |
$POSTMATCH | $' | regex op, to the right of last match |
$LAST_PAREN_MATCH | $+ | regex op, the last group |
$N | regex op, where N is a number of the nth match | |
$0 | aka $PROGRAM_NAME |
testing
- "do NOT mind code duplication in tests" - PragmaticRuby
- assert_, refute_
- _equal, _empty
require_relative "words_from_string" require "minitest/autorun" # runs all the test_* class TestWordsFromString < Minitest::Test def setup # called before each test_ method in class @playlist = Playlist.new("Gonzo") @movie1 = Movie.new("A", 10) @movie2 = Movie.new("B", 9) $stdout = StringIO.new # supress stdout end def test_high_number @playlist.stub(:roll_die, 6) do # override method to return a constant value @playlist.play() assert_equal 11, @movie1.rank end end def test_empty_string # methods starting with "test" automatically run assert_equal([], words_from_string("")) assert_equal([], words_from_string(" ")) end end
operators
+ | sums, concatenate |
=~ | match string against regex, returns start pos/nil |
%w[] | create an array of strings |
%w() | " |
%i[] | " |
<< | appends an element to an array |
:: | scope resolution operator |
#{} | string interpolation |
¦¦= | if LS is falsey, eval RS and assign it to LS |
=> | "hashrocket", associates k/v in a Hash |
require
require "csv" require_relative "book_in_stock" # for "./book_in_stock.rb"
i/o
- https://docs.ruby-lang.org/en/2.4.0/Open3.html
- child process stdin/stdout/stderr and a thread for it
- https://ruby-doc.org/stdlib-2.6.3/libdoc/pathname/rdoc/Pathname.html
- wrapper around File class, crossplatform
- popen - for r/w from/to a process
- system - if you don't care about the output just stdout
- p
- calls #inspect
- pp
- puts
- calls #to_s, to get its string representation
- short for "outPUT String"
- available to all objects
- often written without parentheses
style
- guide https://github.com/airbnb/ruby
- guide https://github.com/rubocop/ruby-style-guide
- tool: linter/formatter https://github.com/standardrb/standard
- tool: linter/formatter https://github.com/rubocop/rubocop/
- 2 spaces for indentation
- book_in_stock.rb (file) / BookInStock (class)
- variable/parameters/methods
- @instance_var @X @_
- @@class_var @@N @@x_pos @@SINGLE
- $global $CUSTOMER $_ $plan9 $Global
- ClassName
- ModuleName
- CONSTANT_NAME
changelog
1.0 - 1996
- 1.6 2000
- 1.8 2003-2013
- 1.9.3 2011
- block local variables
- additional lambda syntax (->(){})
- additional hash literal syntax (colons after the symbol)
- per string encoding
- new socket api
- require_relative
2.0 - 2013
- 2.0
- compatible with 1.9.3
- method keyword arguments
- new method "Module#prepend" to extend a class
- new literal to create array of symbols
- new api for lazy eval of Enumerables
- new convention of using "#to_h" for object to hash conversion
3.0 - 2020
- 3.0
- "3x faster than 2"
- introduces MJIT/YJIT
- new concurrency utils: Fibre, Scheduler, Ractor
- introduces RBS, for typed ruby
- 3.1 2021
- 3.2 2022
- WASM compatible via WASI
- 3.3 2023
- new parser: Prism
- introduces RJIT
control flow
- if/elsif/else/end
- unless/
- while/end
statement modifiers
return nil if user.nil? # guards clause, usually at the beginning of a method puts "Danger" if radiation > 3000 square = 4 square = square * square while square < 1000
- loop/end
- infinite loop
- break out of them
it will also auto-break if the Enumerator inside runs out of values
short_enum = [1,2,3].to_enum long_enum = ('a'..'z').to_enum loop do # loops 3 times puts "#{short_enum.next} - #{long_enum.next}" end
a block
{} do/end
- blocks are closures
- is a chunk of code you can pass to a method, as it were another parameter
assign a block to a variable, all of these return a
Proc
from the blockbo = ->(param) { puts "You called me with #{param}" } # stabby lambda, optional parens bo = lambda { |param| puts "You called me with #{param}" } # Kernel method "lambda", error on wrong nargs bo = proc { |param| puts "You called me with #{param}" } # Kernel method "proc", bubble ups "return" bo = Proc.new { |param| puts "You called me with #{param}" } # same but OLD style bo.call(99)
- can be passed to a method either
- extra IMPLICIT last argument that's passed to a method
(&) EXPLICIT last argument, to call store it and call it later (callbacks)
class ProcExample def pass_in_block(&action) @stored_proc = action end def use_proc(parameter) @stored_proc.call(parameter) # .call stored block end end eg = ProcExample.new eg.pass_in_block { |param| puts "The parameter is #{param}" } eg.use_proc(99)
- parameters to a block are ALWAYS local to that block
- has access to variables outside his scope
Iterator
orEnumerator
a method that can invoke a block of code repeatedly- you can only pass one block per method call greet("dave", "loyal customer") { puts "hi" }
- invoked in a method using
yield
block parameters put after a ; are considered locals to that block (awk-ish) (RARE!)
square = "some shape" sum = 0 [1,2,3,4].each do |value;square| # 2 block parameters square = value * value sum += square end puts sum puts square
no argument
def call_block puts "Start of method" yield yield puts "End of method" end call_block { puts "In the block" }
with arguments
def who_says_what yield("Dave", "hello") yield("Andy", "goodbye") end who_says_what { |person, phrase| puts "#{person} says #{phrase}" }
for
transactions
(ME: using blocks for meta-programming stuff)class File def self.open_and_process(*args) # class method f = File.open(*args) yield f f.close() end end File.open_and_process("testfile", "r") do |file| while line = file.gets puts line end end
Classes
- created with NAME.new
- has/can have
- each instance has an
object_id
property - instance variables
- each instance has an
- attributes are just methods without arguments
- the "only easy" way to change an object's state is by calling onf its methods
- templates for creating objectss
class BookInStock attr_accessor :price # would create a attr_reader/attr_writer attr_reader :isbn #, :price # creates the reader accessor methods #isbn and #price, for @isbn and @price def initialize(isbn, price) # called by BookInStock.new @isbn = isbn @price = Float(price) end # def price=(new_price) # setter # @price = new_price # end def price_in_cents (price * 100).round # !!!! is valid to refer to it either as "price" or "@price" end def price_in_cents=(cents) # setter for a "virtual isntance variable", giving a "uniform access principle" @price = cents / 100.0 end end book = BookInStock.new("isbn1", 33.80) book.price = book.price * 0.75 # using the setter and getter
class File def self.my_open(*args) # CLASS METHOD, parameter list into array "args" file = File.new(*args) # spread array "args", into individual arguments return file unless block_given? # guard return if no block_given? result = yield file file.close result end end
Methods (.)
- private/public/protected def
- can be redefining (just warns about it)
- the verbs of ruby
- attached at the end of variables with by a dot
- some methods (such as
print
) are kernel methods, won't use the dot - parameters
positional
fookeyword
rank: 10 needs the keyword to be passeddefault
rank = 10 with a possible computed default (ME: aka &optional)
- kinds
- module methods (::) self.
- Class Methods (::) self.
- attached after variables and constants by a double colon
- instance methods (.)
- called by RECEIVER.METHODNAME
- a message is being send to the object, which contains the method name along with arguments
- special
- to_s
- inspect
- each
- arguments, keyword args, *,**splats, &arguments
- Types
- public: by default (except initialize which is private) (RARE explicit)
- protected: can be invoked by class or subclasses (RARE!) can be used on attr_reader/w/a
- private: cannot be invoked without an explicit receiver
modules
- to group related constants/methods, or for mixins or namespaces
module Snackbar Snack = Data.define(:name, :price) SNACKS = [ Snack.new("popcorn", 3), Snack.new("candy", 1) ] def self.random_snack # self. declares a "module method" SNACKS.sample end end
require_relative "snackbar" Snackbar::SNACKS.each do { |snack| puts snack.name }
types
- Ruby 2: diagram of classes http://jeromedalbert.com/a-diagram-of-the-ruby-core-object-model/
- Constants
- capitalized
- proper nouns of Ruby
- nil
- is an object, just like any other
- represents the concept of nothing
- Numeric https://ruby-doc.org/3.0.7/Numeric.html
- #positive?, #negative?, #nonzero?, #zero?
Float
https://ruby-doc.org/3.0.7/Float.htmlInteger
https://ruby-doc.org/3.0.7/Integer.html- #times, #upto(N)
- Strings
- "",%{},'',%q{} are string literals, from more to less work done
- "",%{} checks for escape sequences (\) and expression interpolation (#{})
- Both single and double quotes are used to create strings
- #each_char returns an Enumerator if not given a block, you can call .each_with_index on it
- alternatively #each_char.with_index
- Regexp // https://ruby-doc.org/3.2.2/Regexp.html
- the
match operator
=~ can be used to match a string against it returns that starting position or nil- also through String#match? or Regex#match?
- Regexp#sub
- Regexp#gsub
- the
- Symbols :foo
- like lightweight strings
- immutable
- same named symbols have the same
object_id
- used
- as keys and identifiers
- when you need a string but you won't be printing it
- when you need to use the same string over and over
Struct
Snack = Struct.new(:name, :price)
- when you have date and no behaviour
- it creates accessors (r/w) automatically
Data
Snack = Data.define(:name, :price)
- when you have date and no behaviour
- it creates readers automatically
- immutable
Enumerator (class)
- as objects they are
Enumerable
- implements external iterators, where you control the iteration behavior
- Creating
- what an iterator method returns if you don't pass it a block
- #to_enum -> #next
- #enum_for(:each_slice, 3) - in the case the method used for the enumerator takes params
Enumerator.produce - takes an initial value and a block
triangular_numbers = Enumerator.produce([1,2]) do |number,count| [number + count, count + 1] end 5.times { print triangular_numbers.next.first, " " } # => 1 3 6 10 15 p triangular_numbers.first(5).map { _.first } # => [1,3,6,10,15]
Enumerator.new {|x| …}
triangular_numbers = Enumerator.new do |yielder| # called when #next number = 0 count = 1 loop do number += count count += 1 yielder.yield(number) # pausing point end end 5.times { print triangular_numbers.next, " " } # 1 3 6 10 15
Enumerable (module/mixin)
- https://ruby-doc.org/3.2.2/Enumerable.html
#lazy => Enumerator::Lazy reimplements select/map/… to work with infinite sequences
class InfiniteStream def all Enumerator.produce(0) do |number| number += 1 end.lazy end end p InfiniteStream.new.all.first(10) p InfiniteStream.new.select { (_1 % 3).zero? }.first(10) # Example: def palindrome(n) n = n.to_s n == n.reverse end p InfiniteStream.new .select { (_1 % 3).zero? } .select { palindrome?(_1) } .first(10) # Example: alternative syntax multiple_of_three = InfiniteStream.new.all.select { (_1 % 3).zero? } p multiple_of_three.first(10) m3_palindrome = multiple_of_three.select { palindrome?(_1) } p m3_palindrome.first(10) # Example: alternative syntax multiple_of_three = -> n { (n % 3).zero? } palindrome = -> n { n = n.to_s; n == n.reverse } p InfiniteStream.new.all .select(&multiple_of_three) .select(&palindrome) .first(10)
- aka a collection of object references
- #reduce(0) { |sum,ele| sum + ele } #reduce { |sum,ele| sum + ele } #reduce(:+)
- #dig: helps to dig through nested data structures
- Returns if not found nil, doesn't raise an exception
- From: data[:mcu][0][:actors][1] To: data.dig(:mcu, 0, :actors, 1)
Array []
- Enumerable
- [0,2,nil][1] Array.new
- #pop/#push elements from the end
- #shift/#unshift elements from the beginning
- #compact remove nil
- #sample get random element
- %w[] to create array of strings
- %i[] to create array of strings
- [FROM,LENGTH]
- [FROM..END] [FROM…ENDNOTINC]
- can growth by displacing elements with [?,?]= or [?..?]
- [] is a method .[](0) is valid
- #each #reverse_each (#with_index)
- Linear list, accessed by index
- mixed type of objects
- #tally - counts frequency of elements into a hash
Hash {}
- {} {"foo" => "bar"}["foo"] { foo: "bar"}[:foo]
- an indexed collection of key/valuep pairs
- Enumerable
- remembers order of insertion
- supports "punning" for creation, if both are variable names in scope {firstname:, lastname:}
- key/value
- An association by an arbitrary key type
- Hash.new(0) # where 0 is the default value
- #sort_by - returns an array of arrays of 2 elements
stdlib
- new format
- https://ruby-doc.org/3.0.4/standard_library_rdoc.html
- https://docs.ruby-lang.org/en/master/standard_library_rdoc.html
- better colors
- some clickable links
- old format https://ruby-doc.org/stdlib-3.0.4/
- list all versions, without descriptions https://rubydoc.info/stdlib
- guide 2.7 https://rubyreferences.github.io/rubyref/stdlib.html
- https://stdgems.org/
- Every Ruby version ships a specific set of
- default gems
- default libraries
- and bundled gems
- Every Ruby version ships a specific set of
Default Gems in Ruby 3.3.3
name | description |
---|---|
timeout | Auto-terminates code blocks after the time limit is reached |
drb | Distributed object system for Ruby |
nkf | Kanji encoding converter. Can also be used via the Kconv.kconv method. |
strscan | Lexical string scanning |
win32ole | Windows OLE automation interface |
securerandom | Provides cryptographical randomness from openssl or the OS |
english | Readable aliases for special global variables |
fiddle | Support for FFI (Foreign Function Interface) |
erb | Templating engine for Ruby |
tsort | Topological sorting using Tarjan's algorithm, which finds strongly connected components in graphs |
pstore | Transactional file storage for Ruby objects |
cmath | Trigonometric and transcendental functions for complex numbers |
scanf | Pure-Ruby scanf implementation |
prism | Prism is a portable, error tolerant, and maintainable recursive descent parser for the Ruby language |
date | The Date and DateTime classes |
time | Adds more methods to Time |
concurrency
mutex_m | A mixin that makes any object behave like a mutex |
rinda | Support fot the Linda distributed computing paradigm in drb |
thwait | Waits for threads to finish |
sync | Two-phase lock with a counter for multi-threaded code |
io-wait | Adds methods to wait until an IO stream becomes readable or writable |
io-nonblock | Allows to work with IO streams in a non-blocking way |
dev / debug
benchmark | Benchmark utility: Measures and reports the time used to execute code |
bundler | Bundler is the local package manager for Ruby applications |
e2mmap | Abstraction around exception handling |
error_highlight | Improves error messages with additional debug info |
irb | Interactive Ruby Console (REPL) |
prettyprint | Better object formatting and inspection. Also see the pp gem. |
pp | The pp print debugging helper via require 'pp' . It will format the result object in more readable way. |
ruby2_keywords | A shim gemspec to indicate that the Ruby implementation includes the ruby2_keywords method |
rubygems | RubyGems allows you to download, install, and use Ruby software packages on your system |
rdoc | Ruby documentation generator |
syntax_suggest | Assists with code recommendation to resolve Ruby syntax errors |
tracer | Outputs the code execution trace via Kernel#set_trace_func |
datastructures
bigdecimal | Support for arbitrary-precision floating point decimal arithmetic |
delegate | Provides three ways to delegate method calls |
forwardable | Provides a way to delegate method calls. Also see this overview of delegation in Ruby, which contains an example |
set | Data structure for unordered collections without duplicates. Implemented on top of Hash. |
ostruct | Wrapper around Hash which lets you read and set attributes with a method-based API |
observer | Implementation of the observer pattern, away to let interested other objects know o an objetc's updates |
weakref | Explicitly allow objects to be garbage collected |
stringio | Makes strings behave like IO objects |
singleton | Mixin for Ruby classes that should only have one instance |
os
etc | Access UNIX info from /etc |
fcntl | Loads values from the OS' fcntl.h to be used for low-level file descriptor manipulation system calls with IO#fcntl and IO.sysopen |
find | Finds all files in a given directory and its sub-directories |
fileutils | Utilities for working with the file system, such as copying, moving, or deleting files |
open3 | Simple spawning of child processes |
pathname | Wraps File , FileTest , Dir , and FileUtils to ease working with file system paths |
logger | Logging utility |
syslog | Interface to the low-level syslog logger |
tempfile | Simplifies OS independent creation of temporary files |
tmpdir | Adds a Dir.mktmpdir method for creating temporary directories OS independently |
un | Utilities to replace common UNIX commands |
cli
optparse | Command-line option parser |
getoptlong | GNU getopt_long() style command-line option parsing |
io-console | Patches IO for simple and portable access to the console |
reline | Ruby-only implementation of GNU Readline / NetBSD Editline |
readline | If available, readline-ext will be loaded, or (Ruby-only) default gem reline will be used. |
readline-ext | Interface to GNU Readline and NetBSD Editline |
shellwords | Escape and manipulate commands to be run in the shell |
net/http
cgi | Support for CGI (Common Gateway Interface) |
digest | Provides common hash functions like MD5, SHA1, SHA2, or RIPEMD-160 |
net-http | Support for HTTP (Hypertext Transfer Protocol) |
open-uri | Monkeypatches Kernel#open to support remote endpoints via net/http and net/ftp |
openssl | Wraps OpenSSL for cryptographic functionality |
uri | URI/URL manipulation |
webrick | HTTP Server |
net
net-protocol | Internal class for the other net-* libraries |
ipaddr | IP address manipulation (IPv4 and IPv6) |
resolv | Thread-aware DNS resolver |
resolv-replace | Will replace Socket's DNS with resolv |
file formats
csv | Support for CSV (Comma-separated Values) |
dbm | Support for DBM databases |
gdbm | Support for GDBM databases |
json | Support for JSON (JavaScript Object Notation) |
sdbm | Support for SDBM databases |
psych | Support for YAML (YAML Ain't Markup Language) |
yaml | yaml will load the psych default gem |
- | yaml/dbm is a wrapper around DBM using YAML serialization, |
- | yaml/store lets you use pstore with YAML |
zlib | Interface to the zlib compression library |
Default Libraries
continuation | Adds the goto-like Kernel#callcc |
coverage | Measures code coverage |
mkmf | Generates Makefiles for native C extensions |
monitor | Monitors for multi-threaded code |
objspace | Adds more statistics methods to `ObjectSpace`. Since Ruby 3.1, it also includes the `objspace/trace` debug utility. |
pty | Manages pseudo terminals. Also includes the IO#expect method via `require 'expect'`, which can be used to wait for a specific pattern to be read. |
rbconfig | `RbConfig` is a Ruby constant that contains compile time information |
ripper | Ruby parser that creates a symbolic expression tree |
socket | Support for unix- and network sockets |
unicode_normalize | Adds a String#unicode_normalize method which normalizes unicode strings |
win32 | Let's you use Windows APIs, e.g. call functions in DLLs |
Bundled Gems in Ruby 3.3.3
name | description |
---|---|
did_you_mean | Patches error messages to suggest correct spelling of methods/classes/variables |
racc | Bindings for Racc, a YACC-like LALR(1) parser generator |
rake | A Ruby task runner, inspired by make |
debug | Command-line [debugger](https://en.wikipedia.org/wiki/Debugger) |
power_assert | Debug tool that displays intermediate results of a method chain |
test-unit | A xUnit family unit testing framework (compatibility layer for minitest) |
minitest | Test/spec framework, comes with mocking and benchmark capabilities |
rbs | Released with Ruby 3.0, RBS is a way to declare types in Ruby. parsing and processing RBS type definitions |
typeprof | Released with Ruby 3.0, typeprof allows you to automatically generate RBS type annotations |
matrix | Support for [matrices](https://en.wikipedia.org/wiki/Matrix_%28mathematics%29) |
prime | Access to prime numbers and prime factorization |
rss | Support for RSS (Rich Site Summary) and Atom |
rexml | Support for XML (Extensible Markup Language) |
xmlrpc | Remote Procedure Calls via XML and HTTP |
net-ftp | Support for FTP (File Transfer Protocol) |
net-imap | Support for IMAP (Internet Message Access Protocol) |
net-pop | Support for POP3 (Post Office Protocol) |
net-smtp | Support for SMTP (Simple Mail Transfer Protocol) |
net-telnet | Support for Telnet |
concurrency - threads
(1..10).map do |i| Thread.new do `wget http://example.com/file_00#{i}` end end.each { |thread| thread.join }
snippets
`xrandr`.scan(/current (\d+) x (\d+)/) .flatten .map(&:to_i)
1600 | 900 |
"FOO Bar Foo".downcase.scan(/[\w']+/)
foo | bar | foo |
tools
- tool: ri (Ruby API reference frontend)
- tool: version managers
- https://github.com/rbenv/rbenv
- /.ruby-version
- ~/.rbenv/versions/
- commands
- rbenv init
- eval "$(rbenv init - zsh)"
- rbenv install -l
- rbenv install 3.3.0
- rbenv versions
- rbenv rehash
- https://rvm.io/
- https://github.com/postmodern/chruby
- https://github.com/rbenv/rbenv
- tool: seeing is believing
implementations
graalvm | https://github.com/oracle/truffleruby |
jvm | https://www.jruby.org |
mobile | http://www.rubymotion.com/ |
embedded | https://mruby.org/ |