====== Introduction to Ruby ======
==== "A Programmer's Best Friend" ====
----
===== Ruby =====
* Creator: Yukihiro Matsumoto
* Introduced: 1995
* Open source
* Installable package on many UNIX/Linux systems
* **Windows** installer obtainable from http://www.ruby-lang.org
* For **macOS**, installing Ruby through homebrew is recommended: https://www.ruby-lang.org/en/documentation/installation/#homebrew
* Also see https://www.freecodecamp.org/news/do-not-use-mac-system-ruby-do-this-instead
* Can be used for tasks that other dynamic programming languages are used for.
* Initially gained a following due to the popularity of the [[https://wikiless.tiekoetter.com/wiki/Ruby_on_Rails | Ruby on Rails]] web application framework.
* Used by major projects such as [[https://wikiless.tiekoetter.com/wiki/GitLab | GitLab]], [[https://github.blog/2023-04-06-building-github-with-ruby-and-rails | GitHub]], [[https://www.reddit.com/r/rails/comments/z5ricu/how_does_shopify_use_ruby_on_rails/ | Shopify]], etc.
* Used in prominent configuration management systems such as [[https://wikiless.tiekoetter.com/wiki/Ansible_(software) | Ansible]], [[https://wikiless.tiekoetter.com/wiki/Chef_(software) | Chef]] and [[https://wikiless.tiekoetter.com/wiki/Puppet_(company)#Puppet | Puppet]].
* Used as a [[https://wikiless.tiekoetter.com/wiki/Domain-specific_language | domain-specific language]] in Chef and Puppet.
* Popularity/Usage (Stack Overflow Developer Survey)
* https://survey.stackoverflow.co/2024/technology/#most-popular-technologies
* https://survey.stackoverflow.co/2024/technology/#top-paying-technologies
----
===== Ruby Online Resources =====
* http://www.ruby-lang.org - Official site
* http://www.ruby-doc.org/ - Official documentation site
* http://www.ruby-lang.org/en/documentation/ruby-from-other-languages/ - Coming to Ruby from other languages
* https://rubygems.org - The Ruby community’s gem hosting service
* Books:
* http://ruby-doc.org/docs/ProgrammingRuby/ - "Programming Ruby"
* http://www.techotopia.com/index.php/Ruby_Essentials - "Ruby Essentials"
* https://github.com/chriskempson/portable-humble-little-ruby-book - "Humble Little Ruby Book"
* https://nostarch.com/rubywizardry - "Ruby Wizardry (An Introduction to Programming for Kids)"
===== Ruby Execution =====
* Programs typically given .rb extension
* Executed with //ruby prog.rb//
* Or use shell script type line at top of Ruby script
* #!/path/to/ruby
* and make executable with //chmod +x prog.rb//
* Interactive shell by running //irb//
----
===== Ruby: Syntax summary =====
* http://rockhopper.monmouth.edu/~jchung/RubyCheat.pdf (old)
* [[https://dev.to/ericchapman/my-beloved-ruby-cheat-sheet-208o|Another cheat sheet]]
* A quick reference, in html
* http://www.zenspider.com/ruby/quickref.html
===== Ruby: What might be familiar =====
* indentation and whitespace generally not significant
* Perl-like //"expression if bool-expr"// and //"expr unless bool-expr"// can be used.
* ''print'' and ''puts''
* print omits \n at end
* puts includes \n at end
==== Regular expressions ====
* Regular expressions are built-in (like Perl)
* Regexp objects can be built with ''%r|regex|''
* ''%r'' is one of several "[[https://docs.ruby-lang.org/en/3.0/syntax/literals_rdoc.html#label-Percent+Strings|Percent Strings]]" in Ruby.
mypattern = %r|[aeiou]|
* match with =~ or .match()
puts "String has vowels" if "This is a test" =~ /[aeiou]/
puts "String has vowels" if "This is a test".match("[aeiou]")
puts "String has vowels" if "This is a test".match( mypattern )
x = "This is a test".match(/(\w+) (\w+)/)
puts x[0]
puts x[1]
puts x[2]
* substitute with .sub (1 instance) or .gsub (global sub)
puts "foobar".sub('bar', 'foo')
puts "this is a test".gsub('i', '')
x = "This is a test"
puts x.sub(/^../, 'Hello')
* Also see the Ruby string class' [[https://ruby-doc.org/core-3.1.1/String.html#method-i-scan|.scan()]] method.
* Complete documentation on Ruby regex handling is at https://ruby-doc.org/core-3.1.1/Regexp.html
* Includes options for multiline matching and other modifiers.
==== Arrays and hashes ====
* Ruby uses [[http://www.zenspider.com/ruby/quickref.html#arrays|"arrays and hashes"]], like Perl:
x = [1, 2, 3, 4] # array called x
x << 5 # adds 5 to array x
x = { "a" => 1, "b" => 2 } # hash called x
# Ruby-style iteration through hash:
x.each { |key, value| puts "#{key} equals #{value}" }
# The above uses a shorter version of the following 'do' block:
x.each do |key, value|
puts "#{key} equals #{value}"
end
----
===== Ruby: What might not be familiar =====
==== Control structures ====
if bool-expr [then]
body
elsif bool-expr [then]
body
else
body
end
while bool-expr [do]
body
end
for name[, name]... in expr [do]
body
end
==== Variable interpolation ====
* Use #{varname} or #{expression}
x = 10
y = 20
z = "annoying"
puts "#{x} + #{y} = #{x + y}"
puts "This is so #{z.upcase}!"
# also works # puts "This is so " + z.upcase + "!"
==== Multi-line strings ====
* Can create a multi-line string with the ''%q'' percent string
* No variable interpolation can be done within ''%q''.
x = %q{This is a test
of the multi
line capabilities}
* Variable interpolation can be done within ''%Q''
s = 'another'
x = %Q{This is #{s} test
of the multi
line capabilities}
==== Ruby naming conventions ====
* Ruby enforces some naming conventions.
* If an identifier starts with a capital letter, it is a constant.
* Method names, however, are also allowed to start with capital letters.
* If it starts with a dollar sign ($), it is a global variable.
* such as the built-in **$stdin** global object
* See the [[http://rockhopper.monmouth.edu/~jchung/RubyCheat.pdf|cheatsheet]] for other global objects (a.k.a. Predefined Variables).
* If it starts with @, it is an instance or object variable.
* instance vars have scope within the current object
* If it starts with @@, it is a class variable.
* like ''static'' variables in Java
* class vars have scope within the entire class
* Some other conventions
* Boolean methods end in ?
* Hash#has_key? ( key )
* Methods can be made to mutate their argument(s), by appending !
* Str#downcase!
mystring = "Are strings mutable?"
mystring.downcase
puts mystring + " No."
mystring.downcase!
puts "#{mystring} It Depends!"
==== Everything except nil and false considered true ====
# in Ruby, 0 is considered true.
if 0
puts "0 is true"
else
puts "0 is false"
end
==== Ruby-style iteration and blocks ====
# Preferred form
some_list.each do |this_item|
# some statements
end
# In Python: for this_item in some_list:
# In Perl: foreach $this_item (some_list)
* The ''do ... end'' section is called a block and can be shorted to an "inline" block using curly braces {}.
* This is best used for short blocks.
some_list.each do |this_item|
# some statements
end
some_list.each { |this_item| # some statements } # Example of an inline block
* The ''with_index'' method can be used with ''each'' to iterate through an array by index.
* Similar to Python's ''[[https://docs.python.org/3/library/functions.html#enumerate|enumerate()]]'' function
some_list.each.with_index do |item, item_index|
# Sample statement to print numbered list of items:
puts "#{item_index}. #{item}"
end
# Can also specify starting value of item_index:
some_list.each.with_index(1) do |item, item_index|
# Sample statement to print numbered list of items:
puts "#{item_index}. #{item}"
end
==== Functional ops using map and filter ====
* In the functional programming paradigm, the "map" and "filter" operations are important "[[https://wikiless.tiekoetter.com/wiki/Higher-order_function|higher-order]]" functions for processing collections of data.
* The "fold" (or "reduce") operation is a third fundamental higher-order function.
* Ruby does not have [[http://rockhopper.monmouth.edu/cs/jchung/cs498gpl/introduction_to_python#python_list_comprehensions|list comprehensions]] like Python, but methods like ''map'' and ''filter'' may serve the same purpose as list comprehensions, i.e., create lists from lists.
* In Ruby, the ''map'' (or ''collect'') method applies a given block to each item of an iterable (like an array) and returns a new array of the results that is the same size as the source iterable.
# Range of ints from 1 to 10
nums = 1..10
# Map applies a block to every element of an array
# and returns a resulting array of the same size:
nums.map do |num|
num.to_s # returns string copy of num
end
# Assign result of map to an array:
strnums = nums.map do |num|
num.to_s
end
# Map an inline block and replace original Range object with an array:
nums = nums.map { |num| num.to_s }
# Replace original array contents with map!:
nums.map! { |num| num.to_i }
# Array of symbols:
animals = [:mouse, :bat, :pangolin]
# Map with array index included (index starts at 1):
animals.map.with_index(1) { |name, index| "#{index}. #{name}" }
# What is returned when a void function block is mapped?
animals.map.with_index(1) { |name, index| puts "#{index}. #{name}" }
* The ''filter'' (or ''select'') method applies a given block to each item of an iterable (like an array) and returns a new array containing all elements of the original iterable for which the given block returns true.
# Use %w percent string to create a list of strings:
words = %w{You feel a whole lot more like you do
now than you did when you used to}
# Filter words in array that have length <= 3:
words.filter do |word|
word.length <= 3 # boolean block
end
# Shorter filter that modifies original words array:
words.filter! { |word| word.length <= 3 }
==== Symbols ====
* Symbols, a feature of languages such as LISP
* begin with :
* don't contain values or objects, unlike variables
* can be considered literal constants that have no value
current_situation = :good
puts "Everything is fine" if current_situation == :good
puts "PANIC!" if current_situation == :bad
# as opposed to
current_situation = "good"
puts "Everything is fine" if current_situation == "good"
puts "PANIC!" if current_situation == "bad"
* Symbols often used in hash creation
person1 = { :name => "Fred", :age => 20, :gender => :male }
person1 = { :name => "Laura", :age => 23, :gender => :female }
==== Ruby methods (functions) ====
* Methods must be defined before they are called.
* The last expression that is evaluated is automatically returned by the method.
def say_hello( name )
"Hello, " + name # Same as: return "Hello, " + name
end
* Method calls typically leave out the parens
* Future Ruby versions could require parens, so should retain habit of using parens in method calls.
puts say_hello "Chris" # Same as: puts say_hello( "Chris" )
----
===== Ruby Classes and Objects =====
==== Discovering classes and methods ====
* Ruby is sometimes referred to as an "introspective" language. See http://phrogz.net/ProgrammingRuby/ospace.html and https://wikiless.tiekoetter.com/wiki/Type_introspection.
* Finding what class an object belongs to: the ''class'' method
a = "This is a test"
puts a.class
* It is possible to query almost any object in Ruby for the methods that are available to it.
a = "This is a test"
puts a.methods.sort.join(' ')
==== Classes ====
* The Ruby on Rails web app framework necessitates a working knowledge of classes in Ruby.
class Person
attr_accessor :name, :age, :gender
# note use of symbols
end
person_instance = Person.new
person_instance.name = "Robert"
person_instance.age = 52
person_instance.gender = "male"
puts person_instance.name
==== Intro to class inheritance in Ruby ====
class Pet
attr_accessor :name, :age, :gender, :color
end
class Cat < Pet
end
class Dog < Pet
# Dog-specific method
def bark
puts "Woof!"
end
end
class Snake < Pet
attr_accessor :length
# additional attribute for Snake
end
==== Using instance or object variables (@varname) ====
# base class Shape
class Shape
end
class Square < Shape
# consider initialize method to be the "constructor"
def initialize( side_length )
@side_length = side_length
end
# Instance variables are private by default, so
# @side_length is private unless made public with
# attr_accessor :side_length
# Methods are public unless declared private:
def area
@side_length * @side_length
end
def perimeter
@side_length * 4
end
end
class Triangle < Shape
def initialize( base_width, height, side1, side2, side3 )
@base_width = base_width
@height = height
@side1 = side1
@side2 = side2
@side3 = side3
end
def area
@base_width * @height / 2
end
def perimeter
@side1 + @side2 + @side3
end
end
my_square = Square.new( 5 )
my_triangle = Triangle.new( 6, 6, 7.81, 7.81, 7.81 )
puts my_square.area
puts my_square.perimeter
puts my_triangle.area
puts my_triangle.perimeter
# Attempt to change @side_length of my_square:
my_square.side_length = 10 # will fail without an attr_accessor for :side_length
puts my_square.area
puts my_square.perimeter
* Note the lack of an ''attr_accessor'' for these class definitions.
* Without ''attr_accessor'', instance variables are private by default.
==== Using class variables (@@varname) ====
* particularly useful for storing information relevant to all objects of a certain class.
* like static class member variables in C++/Java
* Example: Store the number of objects created so far in a certain class using a class variable.
class Square
def initialize
if defined?( @@number_of_squares )
@@number_of_squares += 1
else
@@number_of_squares = 1
end
end
# A class method begins with the name of the class itself
def Square.count
@@number_of_squares
# "return" is optional
end
end
a = Square.new
puts Square.count
b = Square.new
puts Square.count
----
===== Rubygems: Ruby packaging system =====
* RubyGems is a packaging system for Ruby programs and libraries.
* Provides //gem// command
* Can be thought of as Ruby equivalent of [[cs498gpl:using_the_cpan_module_shell | CPAN]] in Perl and 'pip' in Python.
* Try running
# List all available gems; hit q to quit the more pager
gem list --remote | more
# Download and install gems
gem install nokogiri open-uri sqlite3
* Each individually packaged Ruby library (or application) is called a //gem// or //RubyGem//.
* https://rubygems.org - The Ruby community’s gem hosting service
----
===== Ruby and databases =====
* Databases and dynamic languages
* Perl and Python also have commonly used modules that allow these languages to use SQL databases.
* The techniques learned in Ruby are probably applicable to Perl and Python programs that manipulate databases.
* Most common use is in web applications that use a database back-end for data storage.
* Why databases w/ Ruby?
* The Ruby on Rails web app framework is optimized for the creation of database-driven web apps.
==== SQLite with Ruby ====
* SQLite is a lightweight SQL database that does not require a DB server running in the background.
* Widely-adopted for configuration management, e.g. Firefox, Chrome and related browsers store user profiles, browsing history, etc. in SQLite databases.
* Example: Menu-driven program that allows you to create, manipulate and search a database table.
* See [[https://piazza.com/class_profile/get_resource/m65min8nryeil/m97ztlqww4f6cd|rbdb2.rb]]
* You need to have the ''sqlite3'' gem installed. If you successfully installed the ''rails'' gem, you have the ''sqlite3'' gem.
* After you finish running ''rbdb2.rb'', you will have a ''dbfile'' in the same directory as the Ruby program.
----
===== Ruby on Rails: An introduction =====
* Ruby on Rails (RoR or Rails) is an open source Web application development framework.
* Goal: Make it possible to develop Web applications in an easy, straightforward manner, //with as few lines of code as necessary.//
* For this to be possible, Rails makes assumptions and uses default configurations that work for most Web apps.
* The attraction of Rails is that it removes much of the groundwork needed to develop Web apps.
* Features such as database access, dynamic page elements ([[https://guides.rubyonrails.org/v5.0/working_with_javascript_in_rails.html|using JavaScript]]), templating and data validation are either preconfigured or take only a few lines of code to configure.
==== Rails & the Model-View-Controller architecture ====
* Like many contemporary Web app frameworks, Rails uses the [[https://www.codecademy.com/article/mvc|Model-View-Controller]] (MVC) architecture for organizing application programming.
* Splits Rails apps into three sections: models, views and controllers
* Models
* Used to represent forms of data used by the app and contain the logic to manipulate and retrieve that data.
* //In Rails, a model is represented as a class.//
* Views
* The templates and HTML code that users of the Web app see.
* Turn data into formats that users can view.
* Can output data as HTML for Web browsers, XML, RSS and other formats.
* Controllers
* Form the logic that binds together models, data and views.
* Process input and deliver data for output.
* Call methods that are made available by models and deliver it to the views.
* Contain methods known as //actions// that generally represent each action relevant to that controller, such as "show," "hide," "view," "delete," and so forth.
==== Ruby on Rails: More Information ====
* Ruby on Rails home: http://www.rubyonrails.org/
* A complete production-level Rails system requires Ruby, the Rails framework (the 'rails' and other Rubygems), a Web server such as Apache or nginx, a database such as MySQL or sqlite, Node.js, and more.
* A Rails [[https://guides.rubyonrails.org/getting_started.html#creating-a-new-rails-project-installing-rails|development environment]] requires Ruby, SQLite3, Node.js and Yarn.
==== Sample app - mydiary ====
* See [[cs498gpl/sample_ruby_on_rails_app_-_mydiary]].
----