# Object Orientation in Ruby Part 2 Today's Learning Goals: - Implement the initialize method and understand when/why it's necessary - Understand what the `self` keyword refers to and apply that understanding in different situations - Define class methods and understand when to use a class method vs an instance method ## 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? - We'll get an Error - - What Kind? - ArgumentError - We gave 3 arguments but it expected 0 ArgumentError -> 4 places to look: 1. Given number of arguments 2. Expected number of arguments 3. line number where: method is defined 4. line number where: method is called When we don't have an initialize method and we call new on a class, we'll see both line numbers pointing to the .new method call. When do you get an argument error when you call new and pass arguments if we don't have `initialize` defined. ```rb class Dog def initialize(name, age) @name = name @age = age @breed = breed end end lennon = Dog.new("Lennon Snow", "11 months", "Pomeranian") ``` 1: from /Users/dakotamartinez/Development/flatiron/iterations/SENG-LIVE-062821/Phase-3/starter-code/03_object_orientation_in_ruby_part_2/lib/object_oriented_ruby_continued/dog.rb:22:in `new' /Users/dakotamartinez/Development/flatiron/iterations/SENG-LIVE-062821/Phase-3/starter-code/03_object_orientation_in_ruby_part_2/lib/object_oriented_ruby_continued/dog.rb:2:in `initialize': wrong number of arguments (given 3, expected 2) (ArgumentError) ### What do we need to do or change to get this code to work? ... 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? ```rb class Dog def initialize(name, age, breed) @name = name @age = age @breed = breed end def name @name end def age @age end def breed @breed end end lennon = Dog.new("Lennon Snow", "11 months", "Pomeranian") # binding.pry puts lennon.name puts lennon.age puts lennon.breed ``` ## Activity 1 Rework the computer class from yesterday to accept arguments upon initialization. Refactor the code so that we can simplify the code inside of the `first_computer` and `second_computer` methods. Run the specs using the `rspec` command to track your progress, you'll be complete when all the specs in Part 1 are passing. (15 minutes in breakouts - You'll actually be passing some of the specs from part 2 as well when you complete the exercise.) When we return, be prepared to share & discuss your solution with the group. Confirm: - Created Instances - reworked first_computer and second_computer methods to accept arguments upon instantiation. ### What Errors Did you hit? - git issues with pull/merge - needed to add another parameter to dog's initialize (ArgumentError) - ## Discussion Questions ### What does the initialize method do? - allows you to create an instance with arguments you pass for parameters ### When does the initialize method get called? - whenever you invoke .new on the class in which initialize is defined ### When do we need an initialize method and when can we do without it? - when we want objects to have attached data upon initialization. - if we need to pass arguments we need initialize, - any time we want to run code when a new object is instantiated. **Take 5 minute break after discussion** Code after part 1 ```rb class Computer def initialize(brand, screen_size, model_name, model_year) @brand = brand @screen_size = screen_size @model_name = model_name @model_year = model_year end def brand @brand end def brand=(brand) @brand = brand end def screen_size @screen_size end def screen_size=(screen_size) @screen_size = screen_size end def model_name @model_name end def model_name=(model_name) @model_name = model_name end def model_year @model_year end def model_year=(model_year) @model_year = model_year end def asleep @asleep end def sleep @asleep = true end def wake_up @asleep = false end def back_up @last_backed_up_at = DateTime.now end def last_backed_up_at (@last_backed_up_at && @last_backed_up_at.strftime("on %m/%d/%y at %H:%M")) || "never" end def about_this_computer "This computer is a #{@model_year} #{@brand} #{@model_name} with a #{@screen_size} inch screen. It was last backed up #{last_backed_up_at}" end end # create and return the first computer within this method def first_computer Computer.new( "Apple", 15.0, "Macbook Pro", 2012 ) end # create and return the second computer within this method def second_computer Computer.new( "Apple", 13.0, "Macbook Air", 2015 ) end ``` ## 2nd Segment - Keeping track of the Computers we create ### Group Discussion in Breakouts - Pick a Scribe and have them [fill in the group's answers here](https://hackmd.io/@dlm/classes-and-instances-discussion-062821) When we split into groups, the Breakout room that you joined will be your group number. Find the section of the hackmd file linked above corresponding to your group number and fill in your thoughts there. ### What if we wanted to keep track of all of the computers that we create? Where would we store them and why? - A Class variable that refers to an array - We want it to be available when we call that method? - We want to have access to this variable throughout the entire class? ... ### What's the difference between a class variable and an instance variable? (think of examples and use them to illustrate your answer) - Class variable is available to instances - Instance variable is only accessible to that particular instance - Instance has one @ class has two @@ - referring to instance variables that are not initialized will return nil - referring to class variables that are not initialized will raise a NameError (uninitialized class variable) ... ### What's the difference between a class method and an instance method? (think of examples and use them to illustrate your answer) - When we define a class method the name starts with self. - self refers to the class, so we're defining the method on the class - class methods are defined ON the class - instance methods don't start with self. - they're defined inside the class but NOT ON the class. - How do we tell the difference between class and instance methods? - how they're defined - class methods will start with self. - instance methods won't - how they're called - Class.method - they're called on the class - instance.method - they're called on an instance Example: method to retrieve all dogs: ```rb @@all = [] def self.all @@all end # self would refer to the Dog class. ``` ... ### What does `self` refer to within a class method? - the ... ### What does `self` refer to within an instance method? ... ### Can we access class variables from instance methods? - no?, why not? no reader? Like, if we were to try to access `@@all` from an instance method, would we see a value? yes ... ### Can we access instance variables from class methods? - no, not directly. - We can't just do `@brand` from a class method and expect to get a computer's brand back. ... ```rb lennon = Dog.new("Lennon Snow", "11 months", "Pomeranian") => #<Dog:0x00007fa6068ed2e0 @name="Lennon Snow", @age="11 months", @breed="Pomeranian"> 2.6.6 :004 > lennon.all Traceback (most recent call last): 2: from ./bin/console:14:in `<main>' 1: from (irb):4 NoMethodError (undefined method `all' for #<Dog:0x00007fa6068ed2e0>) ``` NoMethodError 1. Name of the: method 2. Name of the: object its called on 3. Line number where: method was called Beginners tend to focus on just the method and the line number, without getting all of the information from the 2nd piece here. Come together and synthesize discussion, While we go, answer the question: ### How do we determine what the `self` keyword refers to? # Important Takeway Class methods are defined on a class Class methods are called on a class Within Class methods self refers to the class Instance methods are defined in a class (but on it) Instance methods are called on an instance of the class Within an instance self refers to the instance receiving the method call. ### How can we apply our understanding of `self` to fix the issue with `about_this_computer` that occurs when the computer is not backed up? ### 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? instance method #### why? doesn't start with self. we're going to be saving individual computers. ... #### What will we be calling `save` on, an instance or the class? This means we're going to call save on instances of the computer class. So, `save` should be an instance method ... let's code the `save` method together and then breakout again to add some additional functionality. ```rb def save @@all << self end ``` ## Segment 3 - 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 ### Final Discussion questions When did you use the `self` keyword? What code did you write that didn't use `self` but could have used `self` (to call a method accessible in the same scope)? How do you determine what `self` will refer to within a method?