Ruby
Basics
Playing with strings
> "Jimmy".reverse
=> ymmiJ
> "Jimmy".length
=> 5
> "Jimmy" * 5
=> "JimmyJimmyJimmyJimmyJimmy"
> 40.to_s.reverse
=> "04"
Useful functions
- strings: to_s
- integers: to_i
- arrays: to_a
Arrays
> [12, 45, 12].max
=> 45
ticket = [12, 45, 12]
ticket.sort!
Printing
print poem
=> "My toast has flown from my hand
And my toast has gone to the moon.
But when I saw it on television,
Planting our flag on Halley's comet,
More still did I want to eat it."
> poem['toast'] = 'honeydew'
=> "honeydew"
Reversing lines of the poem
methods (lines, bytes, chars)
Get lines collections, turn into array, reverse array, join elements
> poem.lines.to_a.reverse.join
"More still did I want to eat it.\
Planting our flag on Halley's comet,\
But when I saw it on television,\
And my toast has gone to the moon.\
My honeydew has flown from my hand\
"
String manipulation
Find all string methods here
- Exclamation Points.
Methods may have exclamation points in their name, which just means to impact the current data, rather than making a copy. No big deal.
poem.delete! "My"
Square Brackets.
With these, you can target and find things. You can even replace them if necessary.
Chaining methods
lets you get a lot more done in a single command. Break up a poem, reverse it, reassemble it: poem.lines.to_a.reverse.join
> poem.include? "My"
=> True
Hash EQUALS Dictionary
Saving and retrieving from a hash, key value pairs, non-ordered,
> books
=> {}
> books["Gravity's Rainbow"] = :splendid
=> :splendid
> books
=> {"Gravity's Rainbow"=>:splendid}
> books["Gravity's Rainbow"]
=> :splendid
> books.keys
=> ["Gravity's Rainbow"]
New hash with all new ratings set to zero
> ratings = Hash.new(0)
=> {}
Count your reviews
return all unique values in books.. into keys within the new ratings hash
> books.values.each { |rate| ratings[rate] += 1 }
=> [:splendid]
> ratings
=> {:splendid=>1}
Blocks
> 5.times {print "Odelay!"}
=> "OdelayOdelayOdelayOdelayOdelay"
Review
Hashes.
The little 'dictionary' with the curly braces: {}.
Symbols.
Tiny, efficiently reusable code words with a colon: :splendid
Blocks.
Chunks of code which can be tacked on to many of Ruby's methods. Here's the code you used to build a scorecard:
> books.values.each { |rate| ratings[rate] += 1 }
Directories
Checking files in a directory, and using wildcards
> Dir.entries "/"
=> [".", "..", "Home", "Libraries", "MouseHole", "Programs", "Tutorials", "comics.txt"]
> Dir["/*.txt"]
=> ["/comics.txt"]
Open file
> print File.read("/comics.txt")
=> "Achewood: http://achewood.com/
Dinosaur Comics: http://qwantz.com/
Perry Bible Fellowship: http://cheston.com/pbf/archive.html
Get Your War On: http://mnftiu.cc/
"
Copying a file
FileUtils.cp('/comics.txt', '/Home/comics.txt')
Append mode
> File.open("/Home/comics.txt", "a") do |f|
.. f << "Cat and girl: http://catandgirl.com"
.. end
=> #<File:/Home/comics.txt (closed)>
Last changed on
> File.mtime("/Home/comics.txt")
=> 2015-09-12 06:42:23 UTC
> File.mtime("/Home/comics.txt").hour
=> 6
Review
- Files.
Lots of methods exist for editing files and looking around in directories.
- Arguments.
Arguments are a list of things sent into a method, separated by commas.
- Block Changes.
You can use do
and end
as another way to make a code block.
Methods
> def load_comics (path)
.. comics = {}
.. File.foreach(path) do |line|
.... name, url = line.split(': ')
.... comics[name] = url.strip
.... end
.. comics
.. end
=> nil
Use it
comics = load_comics('/comics.txt').
- File.foreach
This method opens a file and hands each line of the file to the block. The line variable inside the do...end block took turns with each line in the file.
- split
A method for strings which breaks the string up into an array, removing the piece you pass in. An axe is laid on the colon and the line is chopped in half, giving us the data to be stored in url and name for each comic.
- strip
This quick method removes extra spaces around the url. Just in case.
Require
> require 'popup'
> Popup.goto "http://bing.com"
Making links using popup
Links
> Popup.make{
.. h1 "My Links"
.. link "Go to Bing", "http://bing.com"
.. }
Todo
> Popup.make do
.. h1 "Thing To Do"
.. list do
.... p "Try out Ruby"
.... p "Ride a tiger"
.... p "(down River Euphrates"
.... end
.. end
Generate links from comics file
> Popup.make do
.. h1 "Comics on the Web"
> list do
.... comics.each do |name, url|
...... link name, url
...... end
.... end
.. end
> def show()
.. Popup.make do
.... h1 "Comics on the Web"
.... list do
...... comics.each do |name, url|
........ link name, url
........ end
...... end
.... end
.. end
> show()
CODING EXPRESSIONS
UNLESS
BAD CODE
if ! tweets.empty?
puts "Timeline":
puts tweets
end
AWESOME CODE
unless tweets.empty?
puts "Timeline":
puts tweets
end
BAD CODE
unless tweets.empty?
puts "Timeline":
puts tweets
else
put "No tweets"
end
AWESOME CODE
if tweets.empty?
put "No tweets"
else
puts "Timeline":
puts tweets
end
NIL IS FALSE-Y
BAD CODE
if attachment.file_path !=nil
attachment.post
end
AWESOME CODE
if attachment.file_path
attachment.post
end
ONLY NIL IS FALSE-Y
- "" is `true`
- 0 is `true`
- [] is `true`
WORSE CODE
# Never evaluated
unless name.length
warn "required!"
end
INLINE CONDITIONALS
BAD CODE
if password.length < 8
fail "Password to short"
end
unless username
fail "No user name set"
end
GOOD CODE
fail "Password too short" if password.length < 8
fail "No user name set" unless username
SHORT-CIRCUIT "AND"
BAD CODE
if user
if user.signed_in?
#...
end
end
GOOD CODE
#if user is null, second part never runs
if user && user.signed_in?
#...
end
SHORT-CIRCUIT ASSIGNMENT
result equals 1 in all cases
result = nil || 1
result = 1 || nil
result = 1 || 2
DEFAULT VALUES - "OR"
BAD CODE
tweets = timeline.tweets
tweets = [] unless tweets
GOOD CODE
# if nill, default to empty array
tweets = timeline || []
SHORT CIRCUIT EVALUATION
Second function not evaluated unless current session is nill
def sign_in
current_session || sign_user_in
end
If then could be more readable
CONDITIONAL ASSIGNMENT
Use to set defauls if no existing value
i_was_set = 1
i_was_set ||= 2
puts i_was_set
> 1
i_was_not_set ||= 2
puts i_was_not_set
> 2
CONDITIONAL ASSIGNMENT
Set defaults if nil
BAD CODE
options[:country] = 'us' if options[:country].nil?
options[:privacy] = true if options[:privacy].nil?
options[:geotag] = true if options[:geotag].nil?
GOOD CODE
options[:country] ||= 'us'
options[:privacy] ||= true
options[:geotag] ||= true
CONDITIONAL RETURN VALUES
BAD CODE
if list_name
options[:path] = "/#{username}/#{list_name}"
else
options[:path] = "/#{user_name}"
end
Assing the value of the if statement
GOOD CODE
options[:path] = if list_name
"/#{user_name}/#{list_name}"
else
"/#{user_name}"
end
REFACTORING
BAD CODE
def list_url(user_name, list_name)
if list_name
url = "https://twitter.com/#{user_name}/#{list_name}"
else
url = "https://twitter.com/#{user_name}"
end
url
end
GOOD CODE
def list_url(user_name, list_name)
if list_name
"https://twitter.com/#{user_name}/#{list_name}"
else
"https://twitter.com/#{user_name}"
end
end
CASE STATEMENT VALUE
client_url = case client_name
when "web"
"http://twitter.com"
when "Facebook"
"http://facebook.com/twitter"
else
nil
end
CASE - RANGES
popularity = case tweet.retweet_count
when 0..9
nil
when 10..99
"trending"
else
"hot"
end
CASE - REGEXPS
tweet_type = case tweet.status
when /\A@\w+/
:mention
when /\Ad\s+\w+/
:direct_message
else
:public
end
equivalent form (more readable)
tweet_type = case tweet.status
when /\A@\w+/ then :mention
when /\Ad\s+\w+/ then :direct_message
else :public
end
Challenge 1
# using unlesss
games = ["Super Mario Bros.", "Contra", "Metroid", "Mega Man 2"]
unless games.empty?
puts "Games in your vast collection: #{games.count}"
end
# inline unless
games = ["Super Mario Bros.", "Contra", "Metroid", "Mega Man 2"]
puts "Games in your vast collection: #{games.count}" unless games.empty?
# nil is false-y
search = "Contra"
games = ["Super Mario Bros.", "Contra", "Metroid", "Mega Man 2"]
search_index = games.find_index(search)
if search_index
puts "Game #{search} Found: #{games[search_index]} at index #{search_index}."
else
puts "Game #{search} not found."
end
# short circuit conditional AND
search = "Super Mario Bros."
games = ["Super Mario Bros.", "Contra", "Metroid", "Mega Man 2"]
matched_games = games.grep(Regexp.new(search))
# Found an exact match
if matched_games.length > 0 && matched_games.include?(search)
puts "Game #{search} found."
end
# conditionaal assignment
## REPLACE: search = "" unless search
## WITH:
search ||= ""
games = ["Super Mario Bros.", "Contra", "Metroid", "Mega Man 2"]
matched_games = games.grep(Regexp.new(search))
puts "Found the following games..."
matched_games.each do |game|
puts "- #{game}"
end
# setting search result only once
search = "Contra"
games = ["Super Mario Bros.", "Contra", "Metroid", "Mega Man 2"]
search_index = games.find_index(search)
search_result = if search_index
"Game #{search} found: #{games[search_index]} at index #{search_index}."
else
"Game #{search} not found."
end
puts search_result
# remove search variable altogether
def search(games, search_term)
search_index = games.find_index(search_term)
if search_index
"Game #{search_term} found: #{games[search_index]} at index #{search_index}."
else
"Game #{search_term} not found."
end
end
games = ["Super Mario Bros.", "Contra", "Metroid", "Mega Man 2"]
puts search(games, "Contra")
# reduce method below to one line
def search_index(games, search_term)
search_index = games.find_index(search_term)
if search_index
search_index
else
"Not Found"
end
end
def search_index(games, search_term)
games.find_index(search_term) || "Not found"
end