# Phase 3 Lecture 3 Object Oriented Ruby Continued
# Object Oriented Ruby Continued
## 1st Segment - Review of Object Instantiation
Throw out questions:
```rb
class Dog
end
lennon = Dog.new("Lennon Snow", "11 months", "Pomeranian")
```
### What will happen if we try to run the code above?
```rb
SyntaxError?
NameError?
NoMethodError?
ArgumentError!
Expected 0 arguments got 3
2: from /Users/dakotamartinez/Development/flatiron/phase3-lectures/03_object_oriented_ruby_continued/lib/object_oriented_ruby_continued/playground.rb:4:in `<top (required)>'
1: from /Users/dakotamartinez/Development/flatiron/phase3-lectures/03_object_oriented_ruby_continued/lib/object_oriented_ruby_continued/playground.rb:4:in `new'
/Users/dakotamartinez/Development/flatiron/phase3-lectures/03_object_oriented_ruby_continued/lib/object_oriented_ruby_continued/playground.rb:4:in `initialize': wrong number of arguments (given 3, expected 0) (ArgumentError)
```
ArgumentError has 4 pieces of information you want to get from it (every time)
1. how many arguments were given
2. how many arguments were expected
3. The file and line number where the method is defined - where the expected number of arguments is coming from
4. The file and line number where the method is called - where the given number of arguments is coming from
In our case the two line numbers are both pointing to the same line.
### What do we need to do or change to get this code to work?
- create instance methods within the class with setters and getters?
Say we want to be able to do:
```rb
lennon.name #=> "Lennon Snow"
```
and
```rb
lennon.age #=> "11 months"
```
and
```rb
lennon.breed #=> "Pomeranian
```
### What do we need to add/change in the class to support this?
...
Finally, let's say we want to be able to create a new Dog like this:
```rb
Dog.new(name: "Lennon Snow", age: "11 months", breed: "Pomeranian")
```
Right now we would get:
```rb
SyntaxError?
NameError?
SymbolError?
ArgumentError
expected 3 received 1
```
How would the class have to change to support this?
```rb
class Dog
attr_reader :name, :breed
attr_accessor :age
def initialize(name:, age:, breed:)
@name = name
@age = age
@breed = breed
end
end
```
## Activity 1
Rework the computer class from yesterday to utilize mass assignment upon initialization. Refactor the code to take advantage of the attribute macros you learned about in labs so we can cut down on the code inside of the class. Run the specs using the `rspec` command to track your progress. (15 minutes in breakouts)
When we return, be prepared to share your solution with the group.
Confirm:
- Created Instances
- reworked first_computer and second_computer methods to accept a hash of attributes on instantiation.
### Feedback
When you make changes to the code and run the specs, make sure to read the result **before continuing work**.
### What Errors Did you hit?
- unexpected end error because there was extra comma somewhere
- remove comma
- FailureError expected an instance to respond to a method :asleep that wasn't working.
- no method called :asleep - you just need reader for asleep: `attr :asleep, :last_backed_up_at`
-
## Discussion Questions
### What does the initialize method do?
Method that gets called when we create a new instance with `.new` on the class: `Dog.new` would call the initialize method in the `Dog` class.
### When does the initialize method get called?
When `new` is invoked on the class in which it's defined.
### When do we need an initialize method and when can we do without it?
We wouldn't need initialize if we didn't need to pass any arguments when creating a new instance.
### If you want to pass arguments when calling `.new` on a class, do you need initialize?
Yes!
**Take 5 minute break after discussion**
## 2nd Segment - Keeping track of the Computers we create
Code so far:
```rb
class Computer
attr_accessor :brand, :screen_size, :model_name, :model_year
attr_reader :asleep, :last_backed_up_at
def initialize(attributes)
# attributes.each do |attribute_name, value|
# self.send("#{attribute_name}=", value)
# end
@brand = attributes[:brand]
@screen_size = attributes[:screen_size]
@model_name = attributes[:model_name]
@model_year = attributes[:model_year]
end
def sleep
@asleep = true
end
def wake_up
@asleep = false
end
def back_up
@last_backed_up_at = DateTime.now
end
def about_this_computer
{
brand: @brand,
screen_size: @screen_size,
model_name: @model_name,
model_year: @model_year,
last_backup_time: @last_backed_up_at.strftime("%m-%e-%y %H:%M")
}
end
end
# create and return the first computer within this method
def first_computer
computer = Computer.new(
brand: "Apple",
screen_size: 15.0,
model_name: "Macbook Pro",
model_year: 2012
)
end
# create and return the second computer within this method
def second_computer
computer = Computer.new(
brand: "Apple",
screen_size: 13.0,
model_name: "Macbook Air",
model_year: 2015
)
end
```
### Group Discussion in Breakouts - Pick a Scribe and have them [fill in the group's answers here](https://hackmd.io/@dlm/phase3-lecture-3-classes-and-instances-discussion-7-21-21)
### What if we wanted to keep track of all of the computers that we create? Where would we store them and why?
...
### What's the difference between a class variable and an instance variable?
...
### What's the difference between a class method and an instance method? (think both about how they're defined and how they're called)
...
### What does `self` refer to within a class method?
...
### What does `self` refer to within an instance method?
...
### Can we access class variables from instance methods?
...
### Can we access instance variables from class methods?
...
### Setting up Activity
Let's say we add a class variable called `@@all` to the computer class so that we can store an array of the computers we create. After we've done that, we can set up a method called `save` that will store a computer inside of the that array.
What kind of method should `save` be?
What will we be calling `save` on, an instance or the class?
let's code the `save` method together and then breakout again to add some additional functionality.
```rb
def save
# insert code here
end
```
## Activity 2 - build an all method that returns an array of all saved computers
Now that we've got a save method built out that will store computer instances within a class variable called `@@all`, your tasks will be to:
- add the `#save` method that will store computer instances in `@@all` (if you haven't already)
- add a method called `.all` that will return all of those computers.
- add a method called `.create` that will:
- accept a hash of attributes (just like initialize)
- use that hash to create a new computer
- save the computer to `@@all`
- return the computer
- add a method called `.backed_up` that will return an array of all computers that have been backed up.
### Final Discussion questions
#### When did you use the `self` keyword?
- defining class methods
- in the save method to save a particular instance
- when you need to refer to the receiving object of a method call
#### What code did you write that didn't use `self` but could have used `self` (to call a method accessible in the same scope)?
- save
- .backed_up you can use `self.all.filter`
#### How do you determine what `self` will refer to within a method?
- the receiving object
- what object did we call the method on? `self` refers to the answer to that question
## Code from end of lecture
```rb
class Computer
attr_accessor :brand, :screen_size, :model_name, :model_year
attr_reader :asleep, :last_backed_up_at
@@all = []
def self.all
@@all
end
def self.create(attributes)
self.new(attributes).save
end
# def self.create(attributes)
# c = self.new(attributes)
# c.save
# c
# end
# def save
# @@all << self
# end
def self.backed_up
self.all.filter{|computer| computer.last_backed_up_at}
end
def initialize(attributes)
# attributes.each do |attribute_name, value|
# self.send("#{attribute_name}=", value)
# end
@brand = attributes[:brand]
@screen_size = attributes[:screen_size]
@model_name = attributes[:model_name]
@model_year = attributes[:model_year]
end
def save
@@all << self
self
end
def sleep
@asleep = true
end
def wake_up
@asleep = false
end
def back_up
@last_backed_up_at = DateTime.now
end
def about_this_computer
{
brand: @brand,
screen_size: @screen_size,
model_name: @model_name,
model_year: @model_year,
last_backup_time: @last_backed_up_at.strftime("%m-%e-%y %H:%M")
}
end
end
# create and return the first computer within this method
def first_computer
computer = Computer.new(
brand: "Apple",
screen_size: 15.0,
model_name: "Macbook Pro",
model_year: 2012
)
end
# create and return the second computer within this method
def second_computer
computer = Computer.new(
brand: "Apple",
screen_size: 13.0,
model_name: "Macbook Air",
model_year: 2015
)
end
```