Intro to Ruby & Web Dev: Method Definition vs. Method Invocation

Method Definition

def method(name)
  @name = name
end

Method Invocation using self.

If you call a method that does not have a caller, ruby will append self. the caller When calling the self. inside an instance method => the instance variable itself is returned.

=> self is an object from within the instance method, self changeses depending on what instance method is calling it. Self being called outside of an instance method refers to the class.

  • Use .inspect to see what self is.
  1. Singleton (ghost) class shadows a particular object, only => will not show up in ancestors +> you can declare methods on objects and is only callable/accessible by that object.
  2. All classes are objects also.
  3. DSL - Doman Specific Language => example Rails is a DSL for Ruby, which gives you colligiual sytanx to work with.
  4. Abtractions - all abstractions leak, not every code base is going fit 100% to a Specific way of writing.

All Classes are Objects also. => All classes are objects of the Class class => Everything that starts with a capital letter is a constant, including classes => that's why we can store methods in them.

Intro to Ruby & Web Dev: Inheritance

Inheritance

Subclasses inherit attributes and behaviors of superclasses.

When in cases of subclasses and superclass with the same class methods, the subclass will overide the superclass because ruby checks an object class before moving to the superclass. n the GoodDog class, we're overriding the speak method in the Animal class because Ruby checks the object's class first for the method before it looks in the superclass. So, that means when we wrote the code sparky.speak, it first looked at sparky's class, which is GoodDog. It found the speak method there and used it.

DRY

Inheritance can be a great way to remove duplication in your code base. There is an acronym that you'll see often in the Ruby community, "DRY". This stands for "Don't Repeat Yourself". It means that if you find yourself writing the same logic over and over again in your programs, there are ways to extract that logic to one place for reuse.

Mixing in Modules

Another way to DRY up your code in Ruby is to use modules. We've already seen a little bit of how to use modules, but we'll give a few more examples here.

Extracting common methods to a superclass, like we did in the previous lesson, is a great way to model concepts that are naturally hierarchical.

Inheritance vs Modules

Now that you know the two primary ways that Ruby implements inheritance, class inheritance and mixing in modules, you may wonder when to use one vs the other. Here are a couple of things to remember when evaluating those two choices.

You can only subclass from one class. But you can mix in as many modules as you'd like. If it's an "is-a" relationship, choose class inheritance. If it's a "has-a relationship, choose modules. Example: a dog "is an" animal; a dog "has an ability to swim. You cannot instantiate modules (i.e., no object can be created from a module) Modules are used only for namespacing and grouping common methods together.

Method Lookup Path

Now that you have a grasp on both inheritance and mixins. Let's put them both together to see how that affects the method lookup path. Recall the method lookup path is the order in which classes are inspected when you call a method.

There are several interesting things about the above output. First, this tells us that the order in which we include modules is important. Ruby actually looks at the last module we included first. This means that in the rare ocurrance that the modules we mix in contain a method with the same name, the last module included will be consulted first. The second interesting thing is that the module included in the superclass made it on to the method lookup path. That means that all GoodDog objects will have access to not only Animal methods, but also methods defined in the Walkable module, as well as all other modules mixed in to any of its superclasses.

Private, Public, and Protected Methods

Public Methods are available to all Non-callable inside or outside a class but can be interpolated Protected methods act as public methods inside the class, therefore can be called but act as private methods outside of the class.

private

def human_years
    self.age / DOG_YEARS
end

sparky = GoodDog.new("Sparky", 4)
sparky.human_years

We get the error message:

NoMethodError: private method `human_years' called for
  #<GoodDog:0x007f8f431441f8 @name="Sparky", @age=28>

We have made the human_years method private by placing it under the private reserved word. So what is it good for if we can't call it? private methods are only accessible from other methods in the class. For example, given the above code, the following would be allowed:

Assume the method definition below is above the "private" keyword

def public_disclosure
  "#{self.name} in human years is #{human_years}"
end

Note that in this case, we can not use self.humanyears, because the humanyears method is private. Remember that self.humanyears is equivalent to sparky.humanyears, which is not allowed for private methods. Therefore, we have to just use human_years. In summary, private methods are not accessible outside of the class definition at all, and are only accessible from inside the class when called without self.

Intro to Ruby & Web Dev: Tic-Tac-Toe Procedural Game

  1. Come up with requirements/specifications - that will determine your own scope.
  2. Application logic, sequence of steps.
  3. Translation of those steps into code.
  4. Run code to verfiy logic.=end

Sequence of steps

  1. Draw a board
  2. Assign Player to "x"
  3. Assign Computer to "o" Loop until a winner or all squares are taken
  4. Player 1 picks an empty square Check for winner
  5. Player 2 picks an empty square
  6. Check for winner
  7. If there is a winner, show winner; else "It's a tie"
require 'pry'

#create a way to allow players to input choice on tictactoe board, hash
def initialize_board
  b = {}
  (1..9).each {|position| b[position] = '  '}
  b
end

#define board position where player/computer choice will go
def draw_board(b)
  system 'clear'
  puts " #{b[1]} | #{b[2]} | #{b[3]} "
  puts "--------------"
  puts " #{b[4]} | #{b[5]} | #{b[6]} "
  puts "--------------"
  puts " #{b[7]} | #{b[8]} | #{b[9]} "
end

def empty_position(b)
  b.select {|k, v| v == '  '}.keys

end

def player_picks_square(b)
  puts "Pick a square (1 - 9):"
  #make sure your gets.chomp is getting the state that your hash key is
  position = gets.chomp.to_i
  b[position] = 'x'
  #binding.pry
end

def computer_picks_square(b)
  position = empty_position(b).sample
  b[position] = 'o'
end

def check_winner(b)
winning_lines = [[1,2,3], [4,5,6], [7,8,9], [1,4,7], [2,5,8], [3,6,9], [1,5,9], [3,5,7]]
winning_lines.each do |line|
    if b[line[0]] == 'x' and b[line[1]] == 'x' and b[line[2]] == 'x'
      return 'Player'
    elsif b[line[0]] == 'o' and b[line[1]] == 'o' and b[line[2]] == 'o'
      return 'Computer'
    else
      return nil
    end
  end
end

#sets local variable b from the initialize method (inner_scope) equal to board, a variable defined in the outter scope
board = initialize_board
draw_board(board)


#loop until
begin
  player_picks_square(board)
  computer_picks_square(board)
  draw_board(board)
  winner = check_winner(board)
end until winner || empty_position(board).empty?

if winner
  puts "#{winner} won!"
else
  puts "It's a tie!"
end

Intro to Ruby & Web Dev: The Object Model

What is OOP?

A way for progammers to deal with large, complex systems. One small change could be catastrophic in terms of the impact it could have on relevant parts within the system. Prorgrammers needed to create containers for code that could be changed without impacting the entire program. To section off code that performs certain procedures so that it became an interation of many small parts as opposed to one massive blob of code dependenies.

Encapsulation

is hiding peices of functionality and making it unavailable to the rest of the code base. It is form of data protection so that the data cannot change or be manipulated without intention. This allows your code to achieve new levels of complexity creating objects and exposin interfaces (i.e. methods) that allow these methods to interact.

Polymorphism

ability for data to be represented as many different types. Allows pre-written code to be used for different purposes.

Inheritance

class inherits the behavior of another class -- referred to as the superclass. This gives Ruby Programmers the ability to define large usabilty and smaller Subclass, with more defined, detailed behavior.

Modules

Modules are similar to classes in the they contain shared behavior, however you cannot create an object with a module. A module, must be mixed in with a class using the reserved word: include. This is called a "mixin" and after used in a module, the behanviors declared in the module are available to the class and its objects.

What are objects?

In Ruby, everything is an object. Objects are created from classes.

Class

Ruby defines attributes and behaviors of its objects in classes

Instantation

creating an object/instance of a class

Module

A collection of behaviors that useable by other classes, called mixins using the word "include"

Example: Create a class, create an object, use a mixin.

module Speak
  def speak(sound)
    puts #{sound}
  end
end

class HumanBeing
  include Speak
end

jamela = Humanbeing.new

jamela.speak('Hello!')

Intro to Ruby & Web Dev: Lesson 2

Items covered:

  1. Classes, Instances, & Objects
  2. Modules
  3. Inheritance
  4. Method Lookup Chain
  5. Procedural vs OOP

Classes & Objects

  • Objects are instantized through classes and contant states and behaviors
  • Classes are the cookie cutters
  • Instance methods encapsulate behaviors and are defined in classes.
  • Instance variables = state
  • Instance methods = behaviors Interesting syntax when using equals, setter:
def set_name=(new_name)
  @name = name
end

jamela = User.new

jamela.set_name = "Jam"

or

def set_name(new_name)
  @name = name
end

jamela.set_name("Jam")

Setters:

You have to use .self when using the setter method within an instance method of the same class. Instance methods are like raw data and you can access them but in general, you always want to use the getters and setters when available for encapsulation and santization.

Class Variables:

Track states that are common across all instances within a class.

#To keep track of number of objects
@@class = 0

def initialize(n, h, w)
  @name = n
  @height = h
  @weight = w
  @@count += 1
end

@@class_variable +=1

Class Methods:

Performs behaviors at the class level

def self.total_count
"The total number of dogs is #{@@count}"
end

puts Dog.total_count

Inheritance

SubClass < SuperClass

object.ancestors shows you the method look-up chain

Namespace

class User < ActiveRecord::Base refers to the inheritance of the Base class inside the ActiveRecord module Just like with Cms::User - refers to the User class in the CMS module.

Modules

Allows you to mixin in behavior. The difference between modules and classes are that modules cannot be instantiated.

module Swimmable
  def swim
  puts "I'm swimming!"
  end
end

In method, you can mixin in the module by typing "include Swimmable". When creating modules you simply add "-able" to the end of your method name and then type "include swimmable" in the method you want to add that behavior.

-In method lookups, a module that is mixin will superecede (be listed first) super classes. This means that if a module and a superclass has the same method, the module method will have precedence. When mixin two modules, the method lookup chain will be effected based on reverse inclusion, meanging the last module included will be looked at first.

The point of modules is extract code that is non-specific to a class... and make it reusable.

Some modules can require methods or behaviors:

module Fetchable
  def fetch
  #{name} is fetching!
  end
end

The above module requires the name getter method look up chain

OOP vs Procedural

Procedural thinks about "what happens next..." and small changes can have big impacts throughout your code.

OOP thinks in terms of objects and how they behave Generally consider classes to be nouns and verbs to be methods. Start by first writing out requirements in pseudo-code.