1. This course is good gauge of one's progress in learning to code. I skimmed through most of the course, even the advanced topics, with ease.
a, b, c = 10, 20, 30
5.downto(1) #=> 5,4,3,2,1
1.upto(5) #=> 1,2,3,4,5
1.step(20, 5) { |n| puts n } #=> 1, 6, 11, 16
words = <<HEREDOC
multi-
line
string
HEREDOC
p 'hello\n #{world}' #=> hello\n #{world}
"A" < "a" #=> true
a = "hello"
a.concat(" world")
a #=> "hello world"
a << " today"
a #=> "hello world today"
"again".prepend("hello ")
a #=> "hello hello world today"
"hello world".capitalize
"hello world".upcase
"HELLO WORLD".downcase
"HeLlO wOrLd".swapcase
"Donald"[100, 4] #=> nil
"".empty? #=> true
"".nil? #=> false
15. a :symbol is essentially a light-weight string, instantiated without most of the String methods, which means it takes up less memory. Although in Ruby 2.5 there doesn't seem to be much difference between them.
"my_string".class.methods - :my_symbol.class.methods
### => [:try_convert, :new]
p 1 == 1 ? true : false #=> true
def make_phone_call(number, area_code=03)
"#{area_code}-#{number}"
end
def rate_my_food(food)
case food
when "steak"
"Delicious"
when "Tacos", "Burritos", "Quesadillas"
"Still pretty delicious"
end
end
def calculate_school_grade(grade)
case grade
when 90..100 then "A"
when 80..89 then "B"
when 70..79 then "C"
else
"F"
end
end
!true #=> false
!!true #=> true
password = 'dominoes'
unless password.include?('a')
puts "It does not include the letter 'a'"
end
i = 0
until i > 9
puts i
i += 1
end
n = 5000
puts "Huge number" if n > 2500
puts "Huge number" unless n < 2500
greeting = "hello"
extraction = 100
letter = greeting[extraction]
letter ||= "Not found"
p letter
#=> "Not found"
numbers = (90..150)
numbers.last
#=> 150
numbers = (90...150)
numbers.last
#=> 149
("A".."z").to_a.each { |character| character }
#=> ["A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", "[", "\\", "]", "^", "_", "\`", "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z"]
names = %w[Jack Jill John]
x = Array.new(3)
#=> [nil, nil, nil]
y = Array.new(3, true)
#=> [true, true, true]
z = Array.new(3, [true, false])
#=> [[true, false], [true, false], [true, false]]
names = ["Tom", "Bob"]
names.fetch(100, "Custom error message")
#=> "Custom error message"
letters = ["a".."j"].to_a
letters[25].nil?
#=> true
empty_array = []
letters.empty?
#=> false
empty_array.empty?
#=> true
ary = (1..5).to_a
ary.shift
#=> 1
ary
#=> [2, 3, 4, 5]
ary.shift(1)
#=> [2]
ary
#=> [3, 4, 5]
ary.unshift(20, 30, 40)
#=> [20, 30, 40, 3, 4, 5]
32. Spaceship operator returns nil if incomparable; between arrays, returns the first result of different elements, not total of elements.
5 <=> 5 #=> 0
5 <=> 10 #=> -1
5 <=> 3 #=> 1
5 <=> [1, 2, 3] #=> nil
[3, 4, 5] <=> [nil, 4, 5] #=> nil
[1, 2, 4] <=> [1, 2, 10] #=> -1
1.is_a?(Integer) == (1.class == Integer)
1.1.is_a?(Float) == (1.1.class == Float)
[1,2,3].is_a?(Array) == ([1,2,3].class == Array)
true.is_a?(TrueClass) == (true.class == TrueClass)
false.is_a?(FalseClass) == (false.class == FalseClass)
nil.is_a?(NilClass) == (nil.class == NilClass)
34. #ri will come in handy one day when there is no more internet (and I have backed up the latest version of Ruby before everything goes dark)
ri.String
ri.String.length
numbers = [3,5,7]
for n in numbers
p n
end
n #=> 7
arr = (1..100).to_a
total = []
arr.each_with_index { |element, index| total << element * index }
numbers = [1,2,3,4,5]
numbers.map {|n| n ** 2} == numbers.collect {|n| n ** 2}
numbers.each do |n|
unless n.is_a?(Integer)
next
else
puts n
end
end
words = %w[Shiny happy People holding Hands]
words.sort { |word1, word2| word1.casecmp(word2) }
#=> ["Hands", "happy", "holding", "People", "Shiny"]
foods = ["Steak", "Tofu", "Vegetables", "Steak Burger", "Tuna", "Kale"]
split = foods.partition { |food| food.include?("Steak") }
#=> [["Steak", "Steak Burger"], ["Tofu", "Vegetables", "Tuna", "Kale"]]
good_food, bad_food = split
good_food
#=> ["Steak", "Steak Burger"]
bad_food
#=> ["Tofu", "Vegetables", "Tuna", "Kale"]
"hello world".split == "hello world".split(" ")
str.each_char { |char| p char }
#=> simply iterates over characters in string
str.split("") == str.chars
#=> Array of characters, including spaces
def sum(*numbers)
sum = 0
numbers.each { |n| sum += n }
sum
end
p sum(3,4,5,6,7,8,9)
lottery = (1..100).to_a.sample(6)
lottery.find { |n| n.odd? }
#=> first odd number
lottery.reverse.find { |n| n.odd? }
46. Array#inject == Array#reduce. In both block methods, each iteration hands over the return value of the previous iteration, unlike #each , which focuses exclusively on the value of the current iteration.
result_of_inject = [10, 20, 30, 40].inject(0) do |previous_value, current_value|
puts "previous value: #{previous_value}"
puts "current value: #{current_value}"
previous_value + current_value
end
#=> 100
result_of_reduce = [10, 20, 30, 40].inject(0) do |previous_value, current_value|
puts "previous value: #{previous_value}"
puts "current value: #{current_value}"
previous_value + current_value
end
#=> 100
result_of_inject == result_of_reduce
#=> true
single_line_inject = [10, 20, 30, 40].inject(0) {|previous, current| previous + current}
#=> 100
single_line_inject_to_proc = [10, 20, 30, 40].inject(:+)
#=> 100
single_line_inject == single_line_inject_to_proc
#=> true
names = %w[Bo Moe Jon]
registrations = [true, false, false]
names.zip(registrations)
#=> [["Bo", true], ["Moe", false], ["Jon", false]]
49. Array#sample returns one element as a string, but if supplied any integer (even 1), it will return an array of strings.
flavours = %w[chocolate vanilla mint strawberry apple cinnamon]
flavours.sample
#=> "apple"
flavours.sample(1)
#=> ["mint"]
flavours.sample(2)
#=> ["apple", "mint"]
[1,2,3] * 3
#=> [1, 2, 3, 1, 2, 3, 1, 2, 3]
[1,2,3] | [3,4,5] == [1,2,3].concat([3,4,5]).uniq
#=> true
[1, 1, 2, 3, 4, 5] & [1, 4, 5, 8, 9]
#=> [1, 4, 5]
menu = {
burger: 3.99,
taco: 5.69,
chips: 0.5
}
menu.fetch(:burger) == menu[:burger]
#=> true
menu[:pizza]
#=> nil
menu.fetch(:pizza, "custom error message")
#=> "custom error message"
shopping_list = {
cheese: 1,
tomatoes: 6,
chicken: 1,
lettuce: 1
}
vegan_shopping_list = {}
shopping_list.length
#=> 4
(vegan_shopping_list.length == 0) == vegan_shopping_list.empty?
#=> true
55. Hash#each returns key-value pairs, Hash#each_key and Hash#each_value iterate over the keys and values respectively.
salaries = {
director: 100_000,
producer: 200_000,
ceo: 300_000
}
salaries == salaries.each { |key_value_pair| key_value_pair }
#=> true
roles = salaries.each_key { |role| role }
# :director
# :producter
# :ceo
salaries.each_value { |salary| salary }
# 100000
# 200000
# 300000
salaries = {
director: 100_000,
producer: 200_000,
ceo: 300_000
}
salaries.keys
#=> [:director, :producer, :ceo]
salaries.values
#=> [100000, 200000, 300000]
fruit_prices = Hash.new("No price found")
fruit_prices[:unlisted_fruit]
#=> "No price found"
58. Hash#sort_by values returns a nested array of the key-value pairs in the alphanumeric order of the values, which can be converted back to a hash with Array#to_h.
vips = {
director: "Thomas Jones",
producer: "Michael Jackson",
ceo: "Andrew Jackson"
}
vips.sort == vips.sort_by { |key, value| key }
#=> true
vips.sort_by {|key, value| value }
#=> [[:ceo, "Andrew Jackson"], [:producer, "Michael Jackson"], [:director, "Thomas Jones"]]
vips.sort_by {|key, value| value }.to_h
#=> => {:ceo=>"Andrew Jackson", :producter=>"Michael Jackson", :director=>"Thomas Jones"}
vips = {
director: "Thomas Jones",
producer: "Michael Jackson",
ceo: "Andrew Jackson"
}
vips.delete(:ceo)
#=> "Andrew Jackson"
vips
#=> {:director=>"Thomas Jones", :producer=>"Michael Jackson"}
60. Hash#select and Hash#reject return a Hash of entries: select returns those that satisfy its condition; reject, that fail to satisfy it.
salaries = {
director: 100_000,
producer: 200_000,
ceo: 300_000
}
salaries.select { |key, value| value > 150_000 }
#=> {:producer=>200000, :ceo=>300000}
salaries.reject { |key, value| value < 150_000 }
#=> {:director=>100000}
salaries = {
director: 100_000,
producer: 200_000,
ceo: 300_000
}
salaries.include?("director")
#=> false
salaries.include?(:director)
#=> true
salaries.include?("director".to_sym)
#=> true
def pass_control
p "inside method"
yield
p "back in method"
end
pass_control {return "in the block now"}
# "inside method"
# LocalJumpError: unexpected return
pass_control {p "in the block now"}
# "inside method"
# "in the block now"
# "back in method"
# => "back in method"
a = (1..5).to_a
b = (6..10).to_a
c = (11..15).to_a
cubes = Proc.new { |n| n ** 3 }
a.map { |n| n ** 3 } == a.map(&cubes)
#=> true
b.map { |n| n ** 3 } == b.map(&cubes)
#=> true
c.map { |n| n ** 3 } == c.map(&cubes)
#=> true
a_cubes, b_cubes, c_cubes = [a, b, c].map { |array| array.map(&cubes)}
#=> [[1, 8, 27, 64, 125], [216, 343, 512, 729, 1000], [1331, 1728, 2197, 2744, 3375]]
a_cubes
# => [1, 8, 27, 64, 125]
b_cubes
# => [216, 343, 512, 729, 1000]
c_cubes
# => [1331, 1728, 2197, 2744, 3375]
def pass_control_without_control_on_condition
p "Inside the method"
yield
p "Back inside the method"
end
pass_control_without_control_on_condition
# "Inside the method"
# LocalJumpError: no block given (yield)
def pass_control_on_condition
p "Inside the method"
yield if block_given?
p "Back inside the method"
end
pass_control_on_condition { p "Inside the block now!" }
# "Inside the method"
# "Inside the block now!"
# "Back inside the method"
#=> "Back inside the method"
65. #yield with arguments takes care of routine operations within a method or function, thus freeing up the developer to concentrate on other, more cognitively demanding operations.
def speak_the_truth(name)
yield name if block_given?
end
speak_the_truth("Alex") { |name| puts "#{name} has been studying for many hours."}
# Alex has been studying for many hours.
def number_evalution(num1, num2, num3)
p "Main method"
yield(num1, num2, num3) if block_given?
p "Main method again"
end
p number_evalution(5,10,15) do |num1, num2, num3|
p num1 ** 2
p num2 ** 3
p num3 ** 4
end
# "Main method"
# 25
# 1000
# 50625
# "Main method again"
def custom_each(ary)
i = 0
while i < ary.length
yield ary[i]
i += 1
end
end
names = %w[John Jack Jill Joe]
numbers = (1..5).to_a
custom_each(names) { |name| p "name length: #{name.length}" }
custom_each(numbers) { |number| p "number squared: #{number ** 2}"}
# "name length: 4"
# "name length: 4"
# "name length: 4"
# "name length: 3"
# "number squared: 1"
# "number squared: 4"
# "number squared: 9"
# "number squared: 16"
# "number squared: 25"
66. Instead of a simple block, a Proc can be passed to a method. Also the Proc can be called with Proc#call.
def greeter
p "Inside the method"
yield
p "Back inside the method"
end
phrase = Proc.new { p "Inside the proc" }
greeter(&phrase)
phrase.call
p ["1", "2", "3"].map { |n| n.to_i } == ["1", "2", "3"].map(&:to_i)
#=> true
true_statement = Proc.new { |name| puts "#{name} is writing a useful review of a Ruby course."}
def talk_about(name, &my_proc)
puts "Let me tell you about #{name}:"
my_proc.call(name)
end
talk_about("Alex", &true_statement)
69. lambda returns an error if the wrong number of arguments is supplied, Proc simply returns nil. Otherwise, they seem to be identical
squares_proc = Proc.new { |n| n** 2 }
squares_lambda = lambda { |n| n** 2 }
p [1, 2, 3].map(&squares_proc) == [1, 2, 3].map(&squares_lambda)
#=> true
p squares_proc.call(5) == squares_lambda.call(5)
#=> true
some_proc = Proc.new { |name, age| puts "#{name}, #{age}" }
some_lambda = lambda { |name, age| puts "#{name}, #{age}" }
p some_proc.call("John")
# John,
# nil
p some_lambda.call("John")
# -:9:in block in <main>': wrong number of arguments (given 1, expected 2) (ArgumentError)
# from -:12:in <main>'
def diet_with_proc
status = Proc.new { return "I gave in!" }
status.call
"Still dieting!"
end
def diet_with_lambda
status = lambda { return "I gave in!" }
status.call
"Still dieting!"
end
p diet_with_proc
#=> "I gave in!"
p diet_with_lambda
#=> "I'm still dieting!"
to_euros = lambda { |dollars| dollars * 0.95 }
to_pesos = lambda { |dollars| dollars * 20.70 }
to_rupees = lambda { |dollars| dollars * 68.13 }
def convert(dollars, currency_lambda)
currency_lambda.call(dollars) if dollars.is_a?(Numeric)
end
p convert(1000, to_euros)
p convert(1000, to_pesos)
p convert(1000, to_rupees)
p [1000, 2000, 3000].map(&to_rupees)
p Time.new
# 2018-05-14 10:00:02 +1000
p Time.now
# 2018-05-14 10:00:02 +1000
p Time.new.class
#=> Time
p Time.now.class
#=> Time
p Time.new.object_id
#=> 47049250547520
p Time.now.object_id
#=> 47049250547420
p Time.new == Time.now
# false
p Time.new(2015, 5, 18, 23, 30, 12)
# 2015-05-18 23:30:12 +1000
p Time.now(2015, 5, 18, 23, 30, 12)
# -:8:in now': wrong number of arguments (given 6, expected 0) (ArgumentError)
# from -:8:in <main>'
p birthday = Time.new(1984, 05, 20)
p birthday.monday?
#=> false
p birthday.sunday?
#=> true
p birthday.dst?
#=> false
p start_of_year = Time.new(2019, 1, 1)
# 2019-01-01 00:00:00 +1100
p days_later_45 = start_of_year + (60 * 60 * 24 * 45)
# 2019-02-15 00:00:00 +1100
p Time.now
# 2018-05-14 10:17:42 +1000
p tomorrow = Time.now + (60 * 60 * 24)
# 2018-05-15 10:17:42 +1000
p Time.now.to_s
# "2018-05-14 11:32:23 +1000"
p Time.now.ctime
# "Mon May 14 11:32:23 2018"
p Time.now.to_a
# [23, 32, 11, 14, 5, 2018, 1, 134, false, "AEST"]
76. Time#parse requires a string formatted "yyyy-mm-dd"; #Time.strptime can use strftime switches to parse a string.
require 'time'
p Time.parse("2016-01-1")
# 2016-01-01 00:00:00 +1100
p Time.strptime("03-04-2000", "%d-%m-%Y")
# 2000-04-03 00:00:00 +1000
File.open("first.txt").each { |line| puts line }
File.open("first.txt", "w") do |file|
file.puts "puts includes a carriage return"
file.write "write does not include a carriage return"
endp ARGV.class
p ARGV
p ARGV[0].class
File.rename("old.txt", "new.txt")
File.delete("file_to_delete.txt") if File.exists?("file_to_delete")
$ ruby argv_demo.rb 3 4 5
p ARGV.class
# Array
p ARGV
# ["3", "4", "5"]
p ARGV[0].class
# String
79. File#require and File#require_relative make the file's contents available to the program; File#load both returns and executes its contents. However, my simple example does not bear this out.
def hello_world_method_in_source_file
puts "hello world"
end
load "hello_world.rb"
#=> true
require_relative("hello_world")
#=> true
require("./hello_world")
#=> true
80. Simple regexp returns index position of the first match only, and is case sensitive. (OBVIOUSLY)
phrase = "The Ruby programming language is amazing!"
p phrase =~ /T/
# 0
p phrase =~ /R/
# 4
p phrase =~ /r/
# 10
81. String#scan returns all matches as an array of strings, which can be counted for actually useful information.
phrase = "The Ruby programming language is amazing!"
p phrase.scan(/e/)
# ["e", "e"]
p phrase.scan(/[eRr]/)
# ["e", "R", "r", "r", "e"]
p phrase.scan(/e/).length
str = "I want to find the sub-string 'find' by the sub-string replace."
p str.sub("find", "replace")
# "I want to replace the sub-string 'find' by the sub-string replace."
p str.gsub("find", "replace")
# "I want to replace the sub-string 'replace' by the sub-string replace."
p str.gsub(/\s/, "")
# "Iwanttofindthesub-string'find'bythesub-stringreplace."
p 5.class.superclass
# Numeric
p 5.class.superclass.superclass
# Object
p 5.class.superclass.superclass.superclass
# BasicObject
p 5.class.superclass.superclass.superclass.superclass
# nil
p "Hello World".methods
# [:include?, :%, :*, :+, :count, :partition, :to_c, :sum, :next, :casecmp, :casecmp?, :insert, :bytesize, :match, :match?, :succ!, :<=>, :next!, :index, :rindex, :upto, :==, :===, :chr, :=~, :byteslice, :[], :[]=, :scrub!, :getbyte, :replace, :clear, :scrub, :empty?, :eql?, :-@, :downcase, :upcase, :dump, :setbyte, :swapcase, :+@, :capitalize, :capitalize!, :undump, :downcase!, :oct, :swapcase!, :lines, :bytes, :split, :codepoints, :freeze, :inspect, :reverse!, :grapheme_clusters, :reverse, :hex, :scan, :upcase!, :crypt, :ord, :chars, :prepend, :length, :size, :start_with?, :succ, :sub, :intern, :chop, :center, :<<, :concat, :strip, :lstrip, :end_with?, :delete_prefix, :to_str, :to_sym, :gsub!, :rstrip, :gsub, :delete_suffix, :to_s, :to_i, :rjust, :chomp!, :strip!, :lstrip!, :sub!, :chomp, :chop!, :ljust, :tr_s, :delete, :rstrip!, :delete_prefix!, :delete_suffix!, :tr, :squeeze!, :each_line, :to_f, :tr!, :tr_s!, :delete!, :slice, :slice!, :each_byte, :squeeze, :each_codepoint, :each_grapheme_cluster, :valid_encoding?, :ascii_only?, :rpartition, :encoding, :hash, :b, :unicode_normalize!, :unicode_normalized?, :to_r, :force_encoding, :each_char, :encode, :encode!, :unpack, :unpack1, :unicode_normalize, :<=, :>=, :between?, :<, :>, :clamp, :instance_variable_set, :instance_variable_defined?, :remove_instance_variable, :instance_of?, :kind_of?, :is_a?, :tap, :instance_variable_get, :public_methods, :instance_variables, :public_method, :singleton_method, :method, :define_singleton_method, :public_send, :extend, :to_enum, :enum_for, :pp, :!~, :respond_to?, :object_id, :send, :display, :nil?, :class, :singleton_class, :clone, :dup, :yield_self, :itself, :tainted?, :taint, :untrust, :untaint, :trust, :untrusted?, :methods, :frozen?, :singleton_methods, :protected_methods, :private_methods, :!, :equal?, :instance_eval, :instance_exec, :!=, :__id__, :__send__]
85. If a class is not instantiated, an instance variable is not instantiated either. (Obvious, I know, but easy for a novice to miss)
class Gadget
def initialize
@username = "User-#{rand(1..100)}"
@password = (("A".."z").to_a | (0..9).to_a).sample(8).join
@product_id = "#{("a".."z").to_a.sample(3)}-#{rand(1..100)}"
end
attr_accessor :username
def show_username
puts "Username: #{@username}"
end
end
phone = Gadget.new
phone.show_username
# Username: User-8
dead_phone = Gadget
dead_phone.show_username
# -:20:in <main>': undefined method show_username' for Gadget:Class (NoMethodError)
86. An instance method has access to @instance_variables and the methods that come bundled in with them.
class Gadget
def initialize
@username = "User-#{rand(1..100)}"
@password = (("A".."z").to_a | (0..9).to_a).sample(8).join
@product_id = "#{("a".."z").to_a.sample(3)}-#{rand(1..100)}"
end
attr_accessor :username
def show_username
puts "Username: #{@username}"
end
end
phone = Gadget.new
p phone.methods - Object.methods
# [:username, :show_username, :username=]
87. An instance method overrides any other method of the same name within the scope of the instantiated object.
class Gadget
def initialize
@username = "User-#{rand(1..100)}"
@password = (("A".."z").to_a | (0..9).to_a).sample(8).join
@product_id = "#{("a".."z").to_a.sample(3)}-#{rand(1..100)}"
end
def to_s
puts "this overrides #to_s"
end
end
phone = Gadget.new
p phone.to_s
# this overrides #to_s
class Gadget
def initialize
@username = "User-#{rand(1..100)}"
@password = (("A".."z").to_a | (0..9).to_a).sample(8).join
@product_id = "#{("a".."z").to_a.sample(3)}-#{rand(1..100)}"
end
def my_instance_method
puts "class of instantiated Gadget: #{self.class}"
puts "object_id of instantiated Gadget: #{self.object_id}"
puts "inspection: #{self.inspect}"
end
end
phone = Gadget.new
phone.my_instance_method
class Gadget
attr_reader :production_number
# read-only
attr_writer :password
# write-only
attr_accessor :username
# read-write
def initialize
@production_number = "#{("a".."z").to_a.sample(3).join}-#{rand(1..100)}"
@password = (("A".."z").to_a | (0..9).to_a).sample(8).join
@username = "User-#{rand(1..100)}"
end
def my_instance_method
puts "class of instantiated Gadget: #{self.class}"
puts "object_id of instantiated Gadget: #{self.object_id}"
puts "inspection: #{self.inspect}"
end
end
phone = Gadget.new
p phone.production_number
# "qpc-16"
p phone.username
# "User-68"
p phone.username = "user1984"
# "user1984"
p phone.password = "password123"
# "password123"
p phone.password
# -:28:in <main>': undefined method password' for #<Gadget:0x00005592cac77650> (NoMethodError)
# Did you mean? password=
90. Modules are like classes, but cannot be instantiated; they are used to store bundles of related operations for access without instantiating a new object. Also, "::" is called a scope resolution operator.
module LengthConversions
WEBSITE = "https://www.udemy.com/learn-to-code-with-ruby-lang/learn/v4/t/lecture/6482622?start=0"
def self.miles_to_feet(miles)
miles * 5820
end
def self.miles_to_inches(miles)
feet = miles_to_feet(miles)
feet * 12
end
end
p LengthConversions::WEBSITE
# "https://www.udemy.com/learn-to-code-with-ruby-lang/"
p LengthConversions.miles_to_feet(100)
# 582000
p LengthConversions.miles_to_inches(100)
# 6984000
91. Mixin is a module that is added to a class, thereby making its variables and parameters available to said class.
class OlympicMedal
include Comparable
MEDAL_VALUES = { "Gold" => 3, "Silver" => 2, "Bronze" => 1}
attr_reader :type
def initialize(type, weight)
@type = type
@weight = weight
end
def <=> (other)
if MEDAL_VALUES[type] < MEDAL_VALUES[other.type]
-1
elsif MEDAL_VALUES[type] == MEDAL_VALUES[other]
0
else
1
end
end
end
bronze = OlympicMedal.new("Bronze", 5)
silver = OlympicMedal.new("Silver", 10)
gold = OlympicMedal.new("Gold", 3)
p bronze > silver
# false
p bronze < silver
# true
p gold >= silver
# true
p gold <= bronze
# false
p silver > bronze
# true
p silver != bronze
# true
p silver == gold
# false
92. include mixes a module into a class and allows class methods to override module methods of the same name.
module Purchaseable
def purchase(item)
"#{item} has been purchased!"
end
end
class Store
include Purchaseable
def purchase(item)
"from the store #{item} has been purchased"
end
end
milkbar = Store.new
p milkbar.purchase("Tasty cheese")
93. prepend mixes a module into a class, but does not allow class methods to override module methods of the same name.
module Purchaseable
def purchase(item)
"#{item} has been purchased!"
end
end
class Store
prepend Purchaseable
def purchase(item)
"from the store #{item} has been purchased"
end
end
milkbar = Store.new
p milkbar.purchase("Tasty cheese")
94. extend mixes a module into a class, but adds the module's methods on the class itself, not as class methods.
module Announcer
def who_am_i
"The name of the class is #{self}"
end
end
class Extender
extend Announcer
end
class Includer
include Announcer
end
class Prepender
prepend Announcer
end
p Extender.new.who_am_i
# NoMethoError
p Extender.who_am_i
# "The name of the class is Extender"
p Includer.new.who_am_i
# "The name of the class is #<Includer:0x000055f27daf9bf8>"
p Includer.who_am_i
# -:20:in <main>': undefined method who_am_i' for Includer:Class (NoMethodError)
p Prepender.new.who_am_i
# "The name of the class is #<Prepender:0x000055fb74c598b8>"
p Prepender.who_am_i
# -:20:in <main>': undefined method who_am_i' for Prepender:Class (NoMethodError)
95. Enumerable is the module that supplies iteration functionalities to String, Number, Array, Hash, and can be mixed in to a new class to the same effect.
class ConvenienceStore
include Enumerable
attr_reader :snacks
def initialize
@snacks = []
end
def add_snack(snack)
snacks << snack
end
def each
snacks.each { |snack| yield snack }
end
end
milkbar = ConvenienceStore.new
milkbar.add_snack("TimTam")
milkbar.add_snack("Freddo")
milkbar.add_snack("Bisli")
p milkbar.snacks
# ["TimTam", "Freddo", "Bisli"]
p milkbar.map {|snack| snack.upcase}
# ["TIMTAM", "FREDDO", "BISLI"]
96. A private method is accessible to other methods within the instantiated object from the class that it is declared in, but not from an outside object, i.e. a private method cannot be called with an explicit receiver; only the current object can receive method.
class Gadget
attr_writer :password
attr_reader :production_number
attr_accessor :username
def initialize(username, password)
@username = username
@password = password
@production_number = generate_production_number
end
def to_s
"Gadget #{@production_number} has the username #{@username}.
It is made from the #{self.class} class and it
has the ID #{self.object_id}"
end
private
def generate_production_number
start_digits = rand(10000..99999)
end_digits = rand(10000..99999)
alphabet = ("A".."Z").to_a
middle_digits = Time.now.year.to_s
5.times { middle_digits << alphabet.sample }
"#{start_digits}-#{middle_digits}-#{end_digits}"
end
end
phone = Gadget.new("user", "password")
p phone.username
# "user"
p phone.production_number
# "64607-2018YHDOW-18212"
p phone.generate_production_number
# -:34:in <main>': private method generate_production_number' called for #<Gadget:0x000055d3408209b0> (NoMethodError)```
### 97. A *protected* method is available to other methods within the same instantiated object AND to other objects instantiated from the same class OR from objects instantiated from *descendents* of said class.
```ruby
class Vehicle
def initialize(age, miles)
base_value = 20000
age_deduction = age * 1000
miles_deduction = miles / 10.to_f
@value = base_value - age_deduction - miles-age_deduction
end
def compare_with_other_vehicle(vehicle)
self.value > vehicle.value ? "Your vehicle is better!" : "Your vehicle is worse!"
end
protected
def value
@value
end
end
class Car < Vehicle
end
civic_vehicle = Vehicle.new(3, 3000)
fiat_vehicle = Vehicle.new(2, 2000)
civic_car = Car.new(3, 3000)
fiat_car = Car.new(2, 200)
p civic_vehicle.compare_with_other_vehicle(fiat_vehicle)
# "Your vehicle is worse!"
p fiat_vehicle.compare_with_other_vehicle(civic_vehicle)
# "Your vehicle is better!"
p civic_car.compare_with_other_vehicle(fiat_car)
# "Your vehicle is worse!"
p fiat_car.compare_with_other_vehicle(civic_car)
# "Your vehicle is better!"
p civic_vehicle.compare_with_other_vehicle(fiat_car)
# "Your vehicle is worse!"
p fiat_vehicle.compare_with_other_vehicle(civic_car)
# "Your vehicle is better!"
97. An instance method is preferable to an instance variable: it keeps instance variables safer and acts as a pseudo-variable. (Modification of an instance variable that does not create a new variable)
class Gadget
attr_writer :password
attr_reader :production_number
attr_accessor :username
def initialize(username, password)
@username = username
@password = password
@production_number = generate_production_number
end
def to_s
"Gadget #{self.production_number} has the username #{self.username}.
It is made from the #{self.class} class and it
has the ID #{self.object_id}"
end
private
def generate_production_number
start_digits = rand(10000..99999)
end_digits = rand(10000..99999)
alphabet = ("A".."Z").to_a
middle_digits = Time.now.year.to_s
5.times { middle_digits << alphabet.sample }
"#{start_digits}-#{middle_digits}-#{end_digits}"
end
end
phone = Gadget.new("user", "password")
puts phone.to_s```
# Gadget 22692-2018MBJMM-33662 has the username user.
# It is made from the Gadget class and it
# has the ID 47133824417920
### 98. *self* can be omitted from being called on an instance variable that is called by an instance method, except on a special Ruby keyword like *class*.
#### I'd prefer to make it explicit, but I guess leaving these sorts of things out is the Ruby way.
```ruby
def to_s
"Gadget #{production_number} has the username #{username}.
It is made from the #{self.class} class and it
has the ID #{object_id}"
end
99. The most secure way to modify an instance variable is via an instance method (with the keyword self) that has access to otherwise inaccessible instance variables.
class Gadget
# initialize etc...
def reset_device(username, password)
self.username = username
self.password = password
self.apps = []
end
end
module AppStore
App = Struct.new(:name, :developer, :version)
APPS = {
App.new(:Chat, :facebook, 2.0),
App.new(:Twitter, :twitter, 5.8),
App.new(:Weather, :yahoo, 10.15)
}
# *self* ensures that the method will look within the module, rather than outside it.
def self.find_app(name)
APPS.find { |app| app.name == name }
end
end
# Within Gadget class
def install
app = AppStore.find_app(name)
# @apps is already initialized as an instance variable
@apps << app unless @apps.include?(app)
end
101. A @@class_variable pertains to the Class as a whole rather than a specific Object instantiated therefrom.
class Bicycle
@@maker = "Biketech"
@@count = 0
def self.description_class
"I'm the blueprint for Bicycle Class"
end
def description_instance
"I'm the instance method description"
end
def self.count
@@count
end
def maker
@@maker
end
def initialize
@@count += 1
end
end
bike = Bicycle.new
bike2 = Bicycle.new
p Bicycle.description_class
# "I'm the blueprint for Bicycle Class"
p Bicycle.description_instance
# -:30:in <main>': undefined method description_instance' for Bicycle:Class (NoMethodError)
# Did you mean? description_class
p bike.description_class
# -:37:in <main>': undefined method description_class' for #<Bicycle:0x000055cfeb796150> (NoMethodError)
# Did you mean? description_instance
p bike.description_instance
# "I'm the instance method description"
p bike.maker
# "Biketech"
p Bicycle.maker
# -:30:in <main>': undefined method maker' for Bicycle:Class (NoMethodError)
p Bicycle.count
# 2
p 1.is_a?(Numeric)
# true
p 1.is_a?(Integer)
# true
p 1.instance_of?(Numeric)
# false
class Manager < Employee
def initialize(name, age, rank)
super(name, age)
@rank = rank
end
end
class Employee
attr_reader :name, :age
def initialize(name, age)
@name = name
@age = age
end
def introduce
"Hi, I'm #{name} and I am #{age} years old."
end
end
class Manager < Employee
def initialize(name, age, rank)
super(name, age)
@rank = rank
end
def introduce
super + " I'm a manager!"
end
end
dan = Manager.new("Dan", 42, "Manager")
p dan.introduce
# "Hi, I'm Dan and I am 42 years old. I'm a manager!"
107. super() fetches the return value of the superclass instance method, without passing on any arguments thereto.
108. super(args) fetches the return value of the superclass instance method, and supplies only those args specified, without necessarily passing on all the arguments supplied by the subclass instance method.
class Car
attr_reader :maker
def initialize(maker)
@maker = maker
end
def drive
"Vroom! Vroom!"
end
end
class Firetruck < Car
def initialize(maker, siren)
# Does not pass on siren variable to superclass.
super(maker)
@siren = siren
end
def drive(speed)
# Does not pass to superclass method the argument supplied here.
super() + "Beep! Beep! I'm driving at #{speed} miles per hour!"
end
end
class Widget < Product
def initialize
super # @@product_counter += 1
@@widget_counter += 1
end
def self.counter
@@widget_counter
end
end
class Gadget < Product
def initialize
super # @@product_counter += 1
@@gadget_counter += 1
end
def self.counter
@@gadget_counter
end
end
w = Widget.new
g = Gadget.new
Product.counter
# 2
Widget.counter
# 1
Gadget.counter
# 1
class Player
def play_game
rand(1..100) > 50 ? "Winner!" : "Loser!"
end
end
bob = Player.new
boris = Player.new
def boris.play_game
"Winner!!"
end
p bob.play_game
# "Winner!"
p boris.play_game
# "Winner!!"
p bob.singleton_methods
# []
p boris.singleton_methods
# [:play_game]
p boris.singleton_class
# #<Class:#<Player:0x00005649364fa558>>
class GnuslashlinuxDistros
attr_accessor :favourite, :hated, :interested, :stallman_approved
def initialize(distros)
@favourite = distros[:favourite]
@hated = distros[:hated]
@interested = distros[:interested]
@stallman_approved = distros[:stallman_approved]
end
end
distro_list = { favourite: "Ubuntu", hated: "Linx Mint", interested: "Arch", stallman_approved: "gNewSense"}
my_distro_picks = GnuslashlinuxDistros.new(distro_list)
p my_distro_picks.favourite
# "Ubuntu"