Ruby
There are many languages used to generate dynamic web content. We’re going to start with Ruby on Rails. In order to break up the long evening, we’re going to do some Ruby for a while.
- Why Ruby?
- Has all the major advantages of php and perl without many of the disadvantages
- Designed from the ground up as OO, thereby avoiding the awkwardness
- Has a number of aspects of functional programming built in.
- When used properly, these make your programs easier to write / more elegant.
- When most people think of functional programming they think of lisp (or, perhaps scheme).
- What does LISP stand for?
- Lots of stupid parenthesis
- At one time functional programming had a bad name, because pure functional programming can make simple things difficult.
- In my opinion, Ruby gives the best of both worlds:
- OO when it is helpful
- functional when it is helpful
- As of 2024, Functional programming paradigms have made their way into other languages
- Javascript
- C#
- Java (albeit poorly)
- What does LISP stand for?
- Learning Ruby will help you effectively utilize newer language features.
- Ruby has a different way of writing code. This is called “idiomatic”
Ruby. Although you can write familiar Java-style code,
- You will miss out on opportunities for better code
- People might laugh at you.
- Key Ruby ideas
-
Basics are not too different from what you’ve seen before:
puts "Hello, World" x = 5 y = 6 z = x + y puts "5 + 6 = #{z}" if z % 2 == 0 puts "#{z} is even" else puts "#{z} is odd" end def greet(first_name) puts "Hello, #{first_name}. It's nice to meet you" greet('George')
- Ruby implementation of common ideas:
- Means of interpolating strings (
#{foo}
) - Use of
end
to terminate blocks - parens are optional for
if
conditions and method calls (most of the time) - Note: In ruby, everything is an object, so there are no function calls, only method calls.
- Means of interpolating strings (
- variables use underscores (e.g.,
multi_word_variable_name
) not “camel-case” (e.g.,doNotNameVariablesLikeThis
)- or
first_name
vs.firstName
- or
- Can interpolate variables into strings using the
#{}
construct-
Interpolation is done in double quotes only:
q = 44 puts 'No Interpolation in single quotes: #{q}'
-
Can place code inside brackets
x = 10 y = 15 puts "#{x} + #{y} = #{x + y}" name = 'bob' puts "Hello, #{name.capitalize}"
-
- Symbols and Hashes
- Symbols are like immutable strings.
- They are typically used wherever you would use a constant string and/or enum in Java or C.
- method parameters that select options by name
build_distribution(:uniform)
orbuild_distribution(:normal)
- passing method and/or variable name as parameters (you’ll see examples of this in Rails.)
- keys for hashes (i.e., associative arrays)
- method parameters that select options by name
- Hashes are associative arrays: arrays where the index doesn’t have to be an integer.
-
Keys for hashes can be anything; but, symbols are most common
# The "key: value" syntax here is a short-cut for initializing hashes. # The old syntax was ":key => value" my_dog = { name: "Spot", age: 4, weight: 14 } # Add a year to the age # Notice the use of a symbol for the hash key. my_dog[:age] += 1 puts "My dog, #{my_dog[:name]} is now #{my_dog[:age]} years old." # Sometimes you will want an empty hash to fill later days_in_month = {} days_in_month[:january] = 31 days_in_month[:feb] = 28
-
- Classes and methods
- Constructor is always named
initialize
- Instance variables begin with
@
and do not need to be explicitly declared. - Method parameters do not need types
- Why do some languages (e.g., Java) require types?
- Last expression of a method becomes its return value
- Don’t use explicit
return
statement unless necessary.
- Don’t use explicit
- Constructor is always named
-
class Stats
# constructor is named "initialize"
def initialize
# instance variables begin with "@"
@values = [] # create an empty array
@sum = 0
@sum_sq = 0
end
# Notice that parameters don't need a type
def add(value)
@values << value
@sum += value
@sum_sq += value * value
end
def mean
# value of last expression becomes return value for the method
@sum.to_f / @values.count # to_f is "to float"
end
def median
sorted_values = @values.sort
mid = @values.count / 2
# notice that the if statement takes the value of the
# last line of the block that is run
if (@values.count % 2 == 0)
(@values[mid] + @values[mid - 1]) / 2.0
else
@values[mid]
end
end # method median
end # class Stats
s1 = Stats.new
s1.add(8)
s1.add(6)
s1.add(7)
puts "Mean is #{s1.mean}. Median: #{s1.median}"
s1.add(5)
puts "Mean is #{s1.mean}. Median: #{s1.median}"
Code blocks
- It is often helpful to be able to pass blocks of code (or methods) as parameters to other methods.
- Recall the discussion on first-class functions and the
filter
function.- In particular, remember that the filter function could either be a separate named function, or a “lambda” – an anonymous function defined inline.
- The use of these anonymous “functions” is so common in Ruby that there is a special syntax for it.
- Every method implicitly accepts a code block as a parameter. (It is technically not a lambda; but
the difference isn’t important.)
-
`filter(students) { s s[:gpa] > 3.0 }
-
- Notice that this block is not passed as a parameter, but is just added at the end of the method call.
- The above
{}
syntax is for short, one-line code blocks. There is also a multi-line version:
filter(students) do |s|
s[:gpa] > 3.0
end
filter(students) do |s|
# You can't be on probation until
#you have completed 30 hours.
if s[:credit_hours] < 30
false
elsif s[:credit_hours] < 60
s[:gpa] < 2.0
elsif s[:credit_hours] < 90
s[:gpa] < 2.25
else
s[:gpa] < 2.5
end
end
- Only one block can be passed using this special syntax. Fortunately, it is exceptionally rare for a method to require more than one block/lambda.
- Ruby programmers use this block syntax everywhere — especially for loops:
def sum_list(numbers)
sum = 0
numbers.each do |num|
sum += num
sum # <---- yes, this is common in Ruby
end
def is_increasing(numbers)
numbers.each_with_index do |num, index|
if index > 0 && num < numbers[index - 1]
return false
end
end
true
end
def is_increasing_v2(numbers)
numbers.each_with_index do |num, index|
return false if index > 0 && num < numbers[index - 1]
end
true
end
# Ruby has an insane number of "convenience method" built-in!
def is_increasing(numbers)
numbers.each_cons(2) do |a, b|
return false if a >= b
end
true
end
- When writing Rails apps, you will use a lot of methods that take a block as input;
but you won’t write many. But if you do, you use
yield
to invoke the block
def filter(list)
answer = []
list.each { |item| answer << item if yield(item) }
answer
end
#
# Again, there is an even more ruby-like way to do this:
#
def filter2(list)
# "inject" is often called "reduce" in other languages.
list.inject([]) do |accumulator, item|
accumulator << item if yield(item)
accumulator
end
- Here is
selection_sort
written in idiomatic Ruby:
def selection_sort(list)
n = list.length
(0...n).each do |i|
min_index = i
# Find the minimum element in the unsorted portion
(i + 1...n).each do |j|
min_index = j if yield list[j], list[min_index]
end
# Swap the found minimum element with the first element of the unsorted portion
list[i], list[min_index] = list[min_index], list[i] unless i == min_index
end
list
end
# Sorted low to high
p selection_sort([8, 6, 7, 5, 3, 0, 9]) { |a, b| a < b}
# Sorted high to low
p selection_sort([8, 6, 7, 5, 3, 0, 9]) { |a, b| a > b}
# Sort dogs by age
dog1 = {name: 'Fido', age: 14, weight: 6}
dog2 = {name: 'Spot', age: 4, weight: 12}
dog3 = {name: 'Rover', age: 12, weight: 22}
p selection_sort([dog1, dog2, dog3]) {|a, b| a[:age] < b[:age]}
- The
map
method converts one array into another. It is especially popular in Ruby
array = [1, 2, 3, 4, 5, 6, 7]
squared = array.map { |i| i * i }
p squared
names = [
{ first: 'George', last: 'Washington' },
{ first: 'John', last: 'Adams' },
{ first: 'Thomas', last: 'Jefferson' },
{ first: 'James', last: 'Madison' },
{ first: 'James', last: 'Monroe' }
]
full_names = names.map { |i| "#{i[:last]}, #{i[:first]}"}
p full_names
- Open classes
-
You can add methods to classes
class String def first self[0] end end puts "Hello".first
- Can also add methods to specific objet instances.
- Rails does this a lot; but,
- Don’t overuse it.
-
- Misc.
- Method names are allowed to end in ‘?’ and ‘!’
- Characters have no semantic value; but, by convention
- ’?’ methods used for methods that ask questions:
nil?
,has_key?
- ’!’ methods used for methods that modify an object
sort
vssort!
- ’?’ methods used for methods that ask questions:
Array
comes with a lot of useful methods:- API
select
include?
compact
count
first
last
select
max
min
push
pop
reject
reverse
slice
uniq
- Sample Ruby files:
- For more practice