Professional Documents
Culture Documents
ICONS
Bad Code
Worse Code
Good Code
Awesome Code
EXPRESSIONS
UNLESS
if ! tweets.empty?
puts "Timeline:"
puts tweets
end
EXPRESSIONS
UNLESS WITH ELSE
unless tweets.empty?
puts "Timeline:"
puts tweets
else
puts "No tweets found - better follow some people!"
end
if attachment.file_path
attachment.post
end
EXPRESSIONS
ONLY NIL IS FALSE-Y
"" treated as true!
0 treated as true!
[] treated as true!
unless name.length
warn "User name required"
end
EXPRESSIONS
SHORT-CIRCUIT - AND
if user
if user.signed_in?
#...
end
end
EXPRESSIONS
SHORT-CIRCUIT ASSIGNMENT
result = nil || 1 1
result = 1 || nil 1
result = 1 || 2 1
EXPRESSIONS
DEFAULT VALUES - OR
tweets = timeline.tweets
tweets = [] unless tweets
EXPRESSIONS
SHORT-CIRCUIT EVALUATION
def sign_in
current_session || sign_user_in
end
i_was_not_set ||= 2
puts i_was_not_set 2
EXPRESSIONS
CONDITIONAL ASSIGNMENT
options[:country] = 'us' if options[:country].nil?
options[:privacy] = true if options[:privacy].nil?
options[:geotag] = true if options[:geotag].nil?
EXPRESSIONS
CONDITIONAL RETURN VALUES
if list_name
options[:path] = "/#{user_name}/#{list_name}"
else
options[:path] = "/#{user_name}"
end
EXPRESSIONS
CONDITIONAL RETURN VALUES
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
EXPRESSIONS
CASE STATEMENT VALUE
client_url = case client_name
when "web"
"http://twitter.com"
when "Facebook"
"http://www.facebook.com/twitter"
else
nil
end
EXPRESSIONS
CASE - RANGES
popularity = case tweet.retweet_count
when 0..9
nil
when 10..99
"trending"
else
"hot"
end
EXPRESSIONS
CASE - REGEXPS
tweet_type = case tweet.status
when /\A@\w+/
:mention
when /\Ad\s+\w+/
:direct_message
else
:public
end
EXPRESSIONS
CASE - WHEN/THEN
tweet_type = case tweet.status
when /\A@\w+/ then :mention
when /\Ad\s+\w+/ then :direct_message
else :public
end
EXPRESSIONS
EXPRESSIONS
METHODS AND CLASSES
OPTIONAL ARGUMENTS
def tweet(message, lat, long)
#...
end
tweet("Practicing Ruby-Fu!", nil, nil)
reference keys
from hash
METHODS
HASH ARGUMENTS
def tweet(message, options = {})
tweet("Practicing Ruby-Fu!",
:lat => 28.55,
:long => -81.33,
:reply_id => 227946 all combined into
) keys show meaning options argument
NAMED ARGUMENTS - HASH
Using Ruby 1.9 hash syntax
tweet("Practicing Ruby-Fu!",
lat: 28.55 ,
long: -81.33 ,
reply_id: 227946 COM BO X2 !
)
tweets = get_tweets(my_list)
if tweets.empty?
alert "No tweets were found!" +
"Are you authorized to access this list?"
end
cant be sure its an error
EXCEPTIONS
def get_tweets(list)
unless list.authorized?(@user)
raise AuthorizationException.new
end
list.tweets
end
METHODS
YOU NEED A CLASS WHEN...
user_names = [
["Ashton", "Kutcher"],
["Wil", "Wheaton"],
["Madonna"]
]
user_names.each { |n| puts "#{n[1]}, #{n[0]}" }
O UTP U T :
your users shouldnt
Kutcher, Ashton
Wheaton, Wil have to deal with
, Madonna edge cases
CLASSES
YOU NEED A CLASS WHEN...
class Name
def initialize(first, last = nil)
@first = first state!
@last = last
end
def format
[@last, @first].compact.join(', ')
end
end
behavior!
CLASSES
YOU NEED A CLASS WHEN...
user_names = []
user_names << Name.new('Ashton', 'Kutcher')
user_names << Name.new('Wil', 'Wheaton')
user_names << Name.new('Madonna')
user_names.each { |n| puts n.format }
O UTP U T :
Kutcher, Ashton
Wheaton, Wil
Madonna edge case handled!
CLASSES
OVERSHARING?
class Tweet attr_accessor :baz
same as
attr_accessor :status, :created_at
def initialize(status) def baz=(value)
@status = status @baz = value
@created_at = Time.new end
end def baz
end @baz
end
#<Tweet:0x000001008c89e8> not so r ea da bl e. . .
class Tweet
def to_s
-op en t h e
"#{@status}\n#{@created_at}" so just re
de f i n e i t!
end
end
class and re
tweet = Tweet.new("Eating lunch.")
puts tweet.to_s
Eating lunch.
2012-08-02 12:20:02 -0700
RE-OPENING CLASSES
You can re-open and change any class.
Beware! You don't know who relies on the old functionality.
You should only re-open classes that you yourself own.
CLASSES
SELF
class UserList list = UserList.new('celebrities')
attr_accessor :name list.name
def initialize(name)
name = name e
re -s e t s t h
end
this j us t
end
oc a l va r i a bl e ! nil
nam e l
CLASSES
ENCAPSULATION
Passing around data as strings and numbers breaks encapsulation.
Places using that data need to know how to handle it.
Individual changes require updates at various places.
CLASSES
ENCAPSULATION
tweet = Tweet.new
class Tweet
tweet.status = "Practicing Ruby-Fu!"
attr_accessor ...
tweet.owner_id = current_user.id
def owner
send_tweet(tweet)
retrieve_user(owner_id)
end
end
one parameter!
def send_tweet(message)
message.owner
...
end
CLASSES
ENCAPSULATION
May not be worth the overhead of a class if all you have is data.
An option hash might suce.
When you have behavior to go with the data, it's time to introduce a class.
CLASSES
VISIBILITY
class User
joe = User.new 'joe'
leo = User.new 'leo'
def up_vote(friend)
bump_karma
joe.up_vote(leo)
friend.bump_karma
end
karma up for joe
def bump_karma
karma up for leo
puts "karma up for #{name}"
end
end
class Video
attr_accessor :title, :size, :url
def to_s
"#{@title}, {@size}"
end
end
INHERITANCE
class Attachment class Video < Attachment
attr_accessor :title, :size, :url attr_accessor :duration
def to_s end
"#{@title}, #{@size}"
end
D R Yer! e s
end much e th o d on l y m a k
class Image < Attachment
if a m bc l a s s ,
or on e s u
end sense f
t h e r e .
class Video < Attachment put it
end
SUPER
class User class Follower < User
def initialize(name) def initialize(name, following)
@name = name @following = following
end end
end def relationship
no @ n a m e !
" follows aplusk"
CLASSES
SUPER
class User class Follower < User
def initialize(name) def initialize(name, following)
@name = name @following = following
end super(name)
end end
Calls def relationship
n i ti a l i z e "#{@name} follows #{@following}"
User#i end
end
Grandparent: 'w00t!'
class Child < Parent Child: 'w00t!'
def my_method(argument)
string = super
"#{string}\nChild: '#{argument}'"
end
end
r ( a r gumen t)
me a s s upe
CLASSES
sa
OVERRIDING METHODS
class Attachment
def preview
case @type
when :jpg, :png, :gif typi c a l c a s e
thumbnail
when :mp3 th e oddb a l l
player
end
end
end
This i s s l ow
CLASSES
OVERRIDING METHODS
class Attachment class Audio < Attachment
def preview def preview
new
thumbnail player subc l a s s !
end end
h a n dl i n g
end
the default end special
CLASSES
HIDE INSTANCE VARIABLES
class User
def tweet_header
[@first_name, @last_name].join(' ')
end
def profile
[@first_name, @last_name].join(' ') + @description
end
end
a lot of repetit
ion when
working with the
se...
CLASSES
HIDE INSTANCE VARIABLES
class User
def display_name
[@first_name, @last_name].join(' ')
end
def tweet_header
display_name
end
def profile
display_name + @description
end
end
t o c h a n g e t h e l og ic
if you n eed
it in j us t o n e pla c e!
CLASSES later, you c a n d o
ACTIVESUPPORT
ACTIVESUPPORT
Install It
$ gem install activesupport
not a dependency,
$ gem install i18n
but it is helpful
Load It
require 'active_support/all'
ACTIVESUPPORT
CORE EXTENSIONS: ARRAY
array = [0, 1, 2, 3, 4, 5, 6]
array.from(4) [4, 5, 6]
array.to(2) [0, 1, 2]
array.in_groups_of(3) [[0, 1, 2], [3, 4, 5], [6, nil, nil]]
array.split(2) [[0, 1], [3, 4, 5, 6]]
ACTIVESUPPORT
CORE EXTENSIONS: DATE
apocalypse = DateTime.new(2012, 12, 21, 14, 27, 45)
apocalypse.at_beginning_of_day
apocalypse.at_end_of_month
apocalypse.at_beginning_of_year
apocalypse.tomorrow
apocalypse.yesterday
difference between
options.diff(new_options)
two hashes
{:password=>"dunno"}
ACTIVESUPPORT
CORE EXTENSIONS: HASH
options = { defaults = {
lang: 'fr', lang: 'en',
user: 'codeschool' country: 'us'
} }
options.reverse_merge(defaults)
{
values from lang: 'fr',
user: 'codeschool',
this hash win country: 'us'
}
ACTIVESUPPORT
CORE EXTENSIONS: HASH
new_options = {user: 'codeschool', lang: 'fr', password: 'dunno'}
new_options.assert_valid_keys(:user, :lang)
ACTIVESUPPORT
INFLECTOR
"#{1.ordinalize} place!"
"1st place!"
ordinalize numbers
"#{2.ordinalize} place."
to strings
"2nd place."
"#{23.ordinalize} place."
"23rd place."
ACTIVESUPPORT
INFLECTOR
pluralize and
"user".pluralize
singularize strings
"users"
ACTIVESUPPORT
INFLECTOR
"ruby bits".titleize capitalize every word
"Ruby Bits"
ACTIVESUPPORT
MODULES
NAMESPACE
IMAG E _ U T ILS.RB
def preview(image)
pollutes global namespace
end
R UN. R B
require 'image_utils'
image = user.image
preview(image)
NAMESPACE
IMAG E _ U T ILS.RB
module ImageUtils
def self.preview(image)
end
R UN. R B
require 'image_utils'
image = user.image
ImageUtils.preview(image)
MIXIN
IMAG E _ U T ILS.RB
module ImageUtils
def preview
end
can access properties
def transfer( destination) on objects from host class
end
end
A VAT A R . R B
Included as instance methods
require 'image_utils' RUN .RB
class Image
image = user.image
include ImageUtils
image.preview
end
ANCESTORS
class Image
include ImageUtils
Adding module to Images
end ancestors chain
Image.ancestors
[Image, ImageUtils, Object, Kernel, BasicObject]
Image.included_modules
[ImageUtils, Kernel]
modules only
MIXINS VS CLASS INHERITANCE
class Post class Shareable
def share_on_facebook end
end
end
class Image
def share_on_facebook
end
end
class Tweet
def share_on_facebook
end
end
MIXINS VS CLASS INHERITANCE
class Post < Shareable class Shareable
end def share_on_facebook
end
end
class Image
include Shareable
end
class Tweet
include Shareable
end
MIXINS VS CLASS INHERITANCE
class Post module Shareable
include Shareable def share_on_facebook
include Favoritable end
end end
class Image
include Shareable module Favoritable
include Favoritable def add_to_delicious
end end
end
class Tweet
include Shareable
include Favoritable
end
MIXIN
class Tweet module Searchable
extend Searchable def find_all_from(user)
end end
end
Tweet.find_all_from('@GreggPollack')
MIXIN
class Tweet class Image
extend Searchable include ImageUtils
end end
def transfer(destination)
end
module ClassMethods
def fetch_from_twitter(user)
end image = user.image
end image.preview
end
Image.fetch_from_twitter('gregg')
HOOKS - SELF.INCLUDED
module ClassMethods
def fetch_from_twitter(user)
end
def clean_up
end
end
end
ACTIVESUPPORT::CONCERN
gem install activesupport
require 'active_support/concern'
class Image
module ImageUtils include ImageUtils
extend ActiveSupport::Concern
end
included do
clean_up } included block is executed in
end the context of the Image class
module ClassMethods
def fetch_from_twitter(user)
end
ActiveSupport::Concern looks
def clean_up
end for a module named ClassMethods
end
end
ACTIVESUPPORT::CONCERN
module ImageUtils class Image
def self.included(base) include ImageUtils
base.extend(ClassMethods) include ImageProcessing
end end
module ClassMethods
def clean_up; end ImageProcessing depends
end
end
on ImageUtils
base is Image class
module ImageProcessing
def self.included(base) calls method on
base.clean_up
end Image class
end
ACTIVESUPPORT::CONCERN
module ImageUtils class Image
def self.included(base) include ImageProcessing
base.extend(ClassMethods)
end
end
module ClassMethods
def clean_up; end
end
end
base is ImageProcessing module
module ImageProcessing
include ImageUtils
def self.included(base)
base.clean_up undefined method error
end
end
ACTIVESUPPORT::CONCERN
module ImageUtils class Image
def self.included(base) include ImageProcessing
base.extend(ClassMethods)
end
end
module ClassMethods
def clean_up; end
end
Dependencies are
end properly resolved
module ImageProcessing base is Image class
extend ActiveSupport::Concern
include ImageUtils
def self.included(base)
base.clean_up
end
end
ACTIVESUPPORT::CONCERN
module ImageUtils class Image
extend ActiveSupport::Concern include ImageProcessing
module ClassMethods end
def clean_up; end
end
end Dependencies are
properly resolved
module ImageProcessing
extend ActiveSupport::Concern
include ImageUtils
included do
clean_up
end
end
MODULES
BLOCKS
USING BLOCKS
words = ['Had', 'eggs', 'for', 'breakfast.']
for index in 0..(words.length - 1)
puts words[index]
end
BLOCKS
DECLARING BLOCKS
s i f th e bl oc k
words.each { |word| puts word } br a c e
is a s i n g le li n e
words.each do |word| if i t s
backward_word = word.reverse do/e n d
puts backward_word mul ti p l e l i n e s
end
h e F IR S T
this is t
o c on ve n ti on s !
of tw
BLOCKS
DECLARING BLOCKS
n d if t h e blo c k
words.each do |word| do / e
m e th i n g
puts word
D OES s o )
end
a s i de e f f e c t
( h a s
s i f y ou r e
brace
backward_words = words.map { |word| word.reverse }
i n g t o u s e
just go
u r n v a l ue
D its ret
is th e SE C O N
this
n ve n ti on s !
of two co
BLOCKS
YIELD
def call_this_block_twice
yield
yield
end
twitter
call_this_block_twice { puts "twitter" }
twitter
tweet
call_this_block_twice { puts "tweet" }
tweet
BLOCKS
YIELD - ARGUMENTS
def call_this_block
yield "tweet"
end
BLOCKS
YIELD - RETURN VALUE
def puts_this_block
puts yield
end
BLOCKS
YIELD
def call_this_block
"foo" "oof"
"oof"
block_result = yield "foo"
puts block_result "oof"
end
BLOCKS
USING BLOCKS
class Timeline a t i o n ,
def list_tweets same ite r
@user.friends.each do |friend| differe n t l og i c
friend.tweets.each { |tweet| puts tweet }
end
end
def store_tweets
@user.friends.each do |friend|
friend.tweets.each { |tweet| tweet.cache }
end
end
end
BLOCKS
YOUR OWN EACH
class Timeline
def each er a ti on
@user.friends.each do |friend|
re-use it
friend.tweets.each { |tweet| yield tweet }
end
end
end vary logic
timeline = Timeline.new(user)
timeline.each { |tweet| puts tweet }
timeline.each { |tweet| tweet.cache }
BLOCKS
ENUMERABLE
e m e n t e d
class Timeline you impl
def each n o w m i x i n
...
each,
end E n umer a bl e
include Enumerable
end
BLOCKS
EXECUTE AROUND
def update_status(user, tweet) def get_list(user, list_name)
begin begin
sign_in(user) sign_in(user)
post(tweet) retrieve_list(list_name)
rescue ConnectionError => e rescue ConnectionError => e
logger.error(e) logger.error(e)
ensure ensure
sign_out(user) sign_out(user)
end end
end end
b ut th e c or e
everyt h i n g
i s d u p l ic a te d!
logi c
BLOCKS
EXECUTE AROUND
def while_signed_in_as(user) while_signed_in_as(user) do
begin post(tweet)
sign_in(user) end
yield
rescue ConnectionError => e
logger.error(e) tweets = while_signed_in_as(user) do
ensure retrieve_list(list_name)
sign_out(user) end
end
end
n j us t c a ll th e
now y ou c a
d w i th a b l oc k !
single me t h o
BLOCKS
EXECUTE AROUND
def while_signed_in_as(user)
sign_in(user)
yield COM BO X2 !
rescue ConnectionError => e
logger.error(e)
ensure
sign_out(user)
end
f or be g i n / en d
no nee d
m eth od!
within a
BLOCKS
BLOCKS