Hack hack hack...

An open journal-- some of it written for you, but most of it is for me.

Ruby Bits

||= Assigns if there is not existing value. Otherwise, the previous assigment will override it. Good way to set defaults if you need them

Refactoring using ||=
1
2
3
options[:country] = 'us' if options[:country].nil?
#can refactor as
options[:country] ||= 'us'
Case statement e.g.
1
2
3
4
5
6
7
8
9
10
def search(games, search_term)
  search_index = case games.find_index(search_term)
    when 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")
Options
1
2
3
4
5
6
7
8
9
10
11
def new_game(name, options= {})
  {
    name: name,
    year: options[:year],
    system: options[:system]
  }
end
game = new_game("Street Figher II",
 year: 1992,
 system: "SNES"
 )

rindex- Returns the index of the last occurrence of the given substring or pattern (regexp) in str. Returns nil if not found. If the second parameter is present, it specifies the position in the string to end the search—characters beyond this point will not be considered.

private methods: all private methods are not accessible for outside objects

Raising Exceptions
1
2
3
4
5
6
7
8
9
10
11
12
13
14
class InvalidGameError < StandardError; end
def new_game(name, options={})
  raise InvalidGameError if name.nil?
  {
    name: name,
    year: options[:year],
    system: options[:system]
  }
end
begin
  game = new_game(nil)
rescue InvalidGameError => e
  puts "There was a problem creating your new game: #{e.message}"
end
splat arguments
1
2
3
4
5
6
def describe_favorites(*games)
  for game in games
    puts "Favorite Game: #{game}"
  end
end
describe_favorites('Mario', 'Contra', 'Metroid')
Classes
1
2
3
4
5
6
7
class Game
  def initialize(name, options={})
    @name = name
    @system = options[:system]
    @year = options[:year]
  end
end

Encapsulation

Passing around data and numbers breaks encapsulation If all you are passing around is data- then an options hash should suffice

Private / Protected Methods

Private methods in Ruby cannot be called with an explicit receiver.

Private
1
2
3
4
5
6
def up_vote(friend)
#in this case, bump karma is private
  bump_karma
  #below will error out
  friend.bump_karma
end

Protected methods in Ruby CAN be called with an explicit receiver, but not outside the class.

Protected
1
2
3
4
5
6
def up_vote(friend)
#in this case, bump karma is private
  bump_karma
  #below will error out
  friend.bump_karma
end

Inheritance / Super

A way to DRY up duplication is to use inheritance ot inherit behavior for two classes

Super looks for definitions in the parent class and applies it in the child class

Active Support

1
2
3
4
5
6
7
8
9
def anniversary(game, years)
  game[:release].advance(years: years)
end

game = {
  name: 'Contra',
  release: DateTime.new(1987, 2, 20, 0, 0, 0)
}
puts anniversary(game, 20)
difference between hashes
1
2
3
4
#Using ActiveSupport, return the difference between Mario's favorite games and Luigis's favorite games by implementing the difference_between method.
def difference_between(player1, player2)
  player1.diff(player2)
end

when you include something. it is included in the ancestor’s class.

Namespacing

putting functions in a module will prevent namespacing conflicts a common pattern is to ‘include’ the module inside of the class… this will expose the module methods as instances methods in the object – which means the methods will have access to the object’s internal properties.

when use mixin versus classical class inheritance. A class can only have one super class inheritance suggests specialization

the extends keyword included as the class methods

use extend to expose class methods as class methods use include to expose class methods as instance methods

if you call extend on an object, then it exposes the methods as instance methods on an object

object extend
1
2
3
4
5
game = Game.new("Contra")
#extend the Playable module for the specific game object
#the module is not available to other objects in that class
game.extend(Playable)
game.play

method hooks allows you to both include and extend modules

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
module LibraryUtils

  def add_game(game)
  end

  def remove_game(game)
  end

#this extends the ClassMethods so that it doesn't need to be called below
  def self.included(base)
    base.extend(ClassMethods)
  end


  module ClassMethods
    def search_by_game_name(name)
    end
  end
end

class AtariLibrary
  include LibraryUtils
  #see-- no extend but still includes the class methods module...
end

Active Support Concern

this allows us to use the include and extend calls without making it so ugly.

ActiveSupport Concern
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
require 'active_support/concern'

module LibraryUtils
#this ensures that the dependencies are properly resolved...
  extend ActiveSupport::Concern

  def add_game(game)
  end

  def remove_game(game)
  end

  module ClassMethods
    def search_by_game_name(name)
    end
  end
end
ActiveSupport Concern
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
module LibraryLoader

  module ClassMethods
    def load_game_list
    end
  end
end

module LibraryUtils

  extend ActiveSupport::Concern

  #included block below is much cleaner
  included do
    load_game_list
  end
end

class AtariLibrary
  include LibraryUtils
end

Some more in depth reading to understand this better… here is an explanation from yehuda katz

Concerning Yourself with ActiveSupport::Concern

From 2010- Tweaking on Rails 3.0: #2 ActiveSupport::Concern

Blocks

Yields
1
2
3
4
5
6
7
8
9
10
11
12
13
14
class Library
  attr_accessor :games

  def initialize(games = [])
    self.games = games
  end

  def each_on_system(system)
    games.each do |game|
    #'yield game'yields the game object
      yield game if game.system == system
    end
  end
end
Yields
1
2
3
4
5
  def list
    games.each do |game|
      puts yield game
    end
  end
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
class Game
  attr_accessor :name, :year, :system
  attr_reader :created_at

  def initialize(name, options={})
    self.name = name
    self.year = options[:year]
    self.system = options[:system]
    @created_at = Time.now
  end

  def play
    emulate do |emulator|
      emulator.play(self)
    end
  end

  def screenshot
    emulate do |emulator|
      emulator.start(self)
      emulator.screenshot
    end
  end

private

def emulate
  begin
  emulator = Emulator.new(system)
  yield emulator
    rescue Exception => e
      puts "Emulator failed: #{e}"
  end
end
end

Comments