footer: © Chris Horn, 2017
slidenumbers: true
autoscale: true
theme: Merriweather, 8
Girl Develop It is here to provide affordable and accessible programs to learn software through mentorship and hands-on instruction.
- GDI Rochester
- GDI Cincinnati - Ryan Dlugosz
- GDI Seattle
- We are here for you!
- Every question is important.
- Help each other.
- Have fun!
^ It's easy to get stuck. This class has a fast pace, with lots of slides, so please stop me when you have questions.
- Chris Horn
- chorn@chorn.com
- See Ruby
- Write Ruby
- Meet people who are learning with you
- Get an overview of Ruby
^ Mostly we're going to write Ruby. ^ You will not be a professional Ruby Developer at the end of this class. ^ You will see what programming is like. ^ This is not a survey course on how Ruby is different from other programming languages.
- Ruby is a powerful language that reads like English
- "I hope to see Ruby help every programmer in the world to be productive, and to enjoy programming, and to be happy. That is the primary purpose of Ruby language." - Yukihiro "Matz" Matsumoto
https://en.wikipedia.org/wiki/Ruby_(programming_language) https://en.wikipedia.org/wiki/Yukihiro_Matsumoto
^ Why Ruby?
- You are not the first to get stuck.
- You are not the first to see that error message.
- Google it.
- Who are you?
- What is your programming experience?
- What is your favorite hobby that has nothing to do with technology?
^ Let's go around the room at introduce ourselves!
- Make a copy of these slides
- https://github.com/chorn/intro_to_ruby
- Take notes on paper
- Take notes in an editor
https://repl.it/languages/ruby
ruby 2.3.1p112 (2016-04-26 revision 54768) [x86_64-linux]
>
A REPL is a Read, Evaluate, and Print Loop.
Type this into the right side and hit return:
1 + 1
^ 1 + 1 is in a different font! It's monospaced and looks like code, which means we're going to start reading stuff in that style as code and not English.
> 1 + 1
=> 2
>
What does >
mean?
What does =>
mean?
- Programs are the steps that computers follow to meet a goal.
1 +* 1
1 +* 1
(repl):1: syntax error, unexpected *
1 +* 1
^
^ We all make mistakes. Typos are part of the process.
- Different for each language
1 +* 1
^ In Ruby, lots of things you might think are important to Syntax, are not important. Stuff like whitespaces (spaces, tabs, blank lines) tend not to matter at all. ^ We're normally very specific about how we use whitespace because it makes the code easier to read, and read, and read.
What do these do?
1 + 1
2 * 2
3 - 3
4 / 2
2 / 4
1 / 0
2 ** 3 # Exponents
3 % 2 # Modulo or Remainder
^ # is a Comment.
2 +++ 2
- Similar across languages
^ Not everything that "works" will do what you want it to do.
Ada Lovelace is generally regarded as the first computer programmer. She was a mathematician and wrote an algorithm that a machine could use to generate a specific series of numbers. The background of this slide are her notes--the first computer program.
https://en.wikipedia.org/wiki/Ada_Lovelace
puts "Welcome to Ruby!"
- What is
puts
? - What is
""
^ Strings!
"Ruby".upcase
"Ruby".downcase
"ruby".capitalize
"Ruby".reverse
"Ruby".length
"Ruby".swapcase
"Ruby".empty?
"".empty?
^ These are all messages. These are messages, to Objects. "Ruby" is an Object.
We translate ideas into code
^ What is programming?
Objects are our ideas, as code, put into practice.
^ Welcome to Object Oriented Programming! (OOP)
Sending and receiving messages is the fundamental concept of Object Orient Programming.
^ By the way, Ruby is very very OO. You can think of if as asking questions and getting answers, or making a call and getting a return. We might use different terms and different times, to mean different subtle kinds of messages, but ultimately it's still messages back and forth.
1
is an Object+
is a message (or in Ruby, a method)1
is an argument to+
(and also an Object)
1 + 1
1 + (1)
1 .+ (1)
1.+(1)
^ () is syntax, and in Ruby, often not necessary. We use them for clarity.
puts
isn't really justputs
Kernel.puts
Kernel.rand
^ Whoa.
1 + 2 + 3
"Ruby".upcase.reverse
- Match the semantics of our code to the idea
- I keep saying
Object
- We code in a
Class
^ Class
is a kind of Object
, and it's meant to contain an idea, or a part of an idea.
- Let's make a brick!
- Type this into the left pane of and hit Run
class Brick
end
^ This is a good time to open settings (in the top left) and turn off autocomplete.
- Update the left pane to look like this and hit Run this:
class Brick
end
Brick.new
^ You've defined what a Brick
, and new
is the message you give to the Class
so that you get back your own Brick
to use.
- What questions do I want to ask a
Brick
? - What properties do all
Brick
s have? - What properties do some
Brick
s have?
Methods are the names of our messages.
class Brick
def color
"gold"
end
end
Brick.new.color
^ Don't type Brick.color
it will be bad for your brain right now.
- One with a
String
- Another with an
Integer
- What worked?
- What didn't work?
class Brick
def color
"gold"
end
def length
8
end
def width
4
end
def height
2
end
def location
"On the table."
end
end
Brick.new.color
Brick.new.length
Brick.new.width
Brick.new.height
Brick.new.location
true
false
!
true
false
!true
!false
- Method names that end in a
?
- Return
true
orfalse
- What worked?
- What didn't work?
class Brick
def color
"gold"
end
def length
8
end
def width
4
end
def height
2
end
def location
"On the table."
end
def heavy?
true
end
def edible?
false
end
end
Brick.new.color
Brick.new.length
Brick.new.width
Brick.new.height
Brick.new.location
Brick.new.heavy?
Brick.new.edible?
- What have we learned?
- What's missing?
Homework
- What is an algorithm?
- Welcome back!
- Syntax & Semantics
- Object Oriented Programming
- Match the semantics of our code to the idea
- Integers
- Strings
- Booleans
- Classes
- Update the left pane to look like this and hit Run this:
class Brick
def color
"gold"
end
end
my_brick = Brick.new
puts my_brick.color
^ What is my_brick
?
^ If our Domain is a Store that sells Bricks...
my_favorite_number = 5
- Can change
- Has a name
- Has a value
- Has a
Class
^ =
means assignment
my_favorite_number = 5
the_worst_day = "Tuesday"
^ Aha, we've been using literals all along!
What does this do?
my_favorite_number = 5
my_favorite_number = 7
my_favorite_number += 1
my_favorite_number *= 2
my_favorite_number = 5
your_favorite_number = my_favorite_number
your_favorite_number = my_favorite_number + 1
your_favorite_number = my_favorite_number * 1
- Code is a mashup of natural language (English), math, and logic
- Programming is powerful because it allows you to combine lots and lots and lots of expressions
^ Without expressions, programming would be really boring, and not very helpful.
greeting = "Hello"
language = "Ruby"
What does this code do?
greeting + language
greeting * language
greeting * 2
language + 2
greeting.upcase.reverse
language.downcase * greeting.length
- So far we've been using
Brick.new.some_method_name
^ How do we make Brick
behave more like String
?
^ How do we give our own brick a property, an attribute that can change?
- Type this into the left pane
class Brick
end
my_brick = Brick.new
^ Comment out any other methods you've got.
class Brick
def initialize
end
end
my_brick = Brick.new
^ Wait why doesn't the new
message call new
? Why did we make initialize
?
class Brick
def initialize
@color = "gold"
end
end
my_brick = Brick.new
@
creates an instance variable@color
exists and is accessible throughout myClass
^ So that the methods of your Class
can share information.
^ This is a property, or Attribute that is internal to your instance of a brick.
class Brick
def initialize
@color = "gold"
end
end
my_brick = Brick.new
puts my_brick.@color
class Brick
def initialize
@color = "gold"
end
def color
@color
end
end
my_brick = Brick.new
puts my_brick.color
^ @color is the same!
class Brick
def initialize
my_color = "gold"
end
def color
my_color
end
end
my_brick = Brick.new
puts my_brick.color
^ Without the @
the Scope of your variable is limited to inside the method.
^ color
is only assigned "gold"
inside of initialize
, and has no value in color
.
class Brick
def initialize
@qwerty = "gold"
end
def color
@qwerty
end
end
my_brick = Brick.new
puts my_brick.color
- What happens?
- Is this a good idea?
my_string = String.new("Hello Ruby")
my_empty_string = String.new()
^ We've been using literal strings, but we can use new
on String
too.
class Brick
def initialize(color)
@color = color
end
def color
@color
end
end
my_brick = Brick.new("gold")
puts my_brick.color
Let's rewrite our use of
Brick
as plain English.
- So far we have
- I would like to make a gold colored brick.
- What is the color of my brick?
- Let's add
- Who owns my brick?
- What is my brick made of?
Change
initialize
, addowner
class Brick
def initialize(color, owner)
@color = color
@owner = owner
end
def color
@color
end
def owner
@owner
end
end
my_brick = Brick.new("gold", "Me!")
puts my_brick.color
puts my_brick.owner
^ The stuff at the bottom is our tests!
- Is it:
my_brick = Brick.new("gold", "Me!")
- Or:
my_brick = Brick.new("Me!", "gold")
^ We are saying that every Brick
will always have a color and an owner, and in order to make and use a Brick
you'll need to remember the order and never mix them up.
^ This is the method signature.
class Brick
def initialize(color:, owner:)
@color = color
@owner = owner
end
def color
@color
end
def owner
@owner
end
end
my_brick = Brick.new(color: "gold", owner: "Me!")
puts my_brick.color
puts my_brick.owner
^ Leave your color
and owner
methods in place.
^ This is kind of magicky, but the benefits of Named Parameters outweigh our confusion of color:
turning into a variable named color
inside our method.
class Brick
attr_reader :color
attr_reader :owner
def initialize(color:, owner:)
@color = color
@owner = owner
end
end
my_brick = Brick.new(color: "gold", owner: "Me!")
puts my_brick.color
puts my_brick.owner
^ Now delete your color
and owner
methods.
What does this do?
my_brick = Brick.new(color: "gold", owner: "Me!")
puts my_brick.color
puts my_brick.owner
my_brick.owner = "You?"
puts my_brick.owner
^ How do we make that work?
class Brick
attr_reader :color
attr_reader :owner
attr_writer :owner
def initialize(color:, owner:)
@color = color
@owner = owner
end
end
my_brick = Brick.new(color: "gold", owner: "Me!")
puts my_brick.color
puts my_brick.owner
my_brick.owner = "You?"
puts my_brick.owner
^ The attr_* lines tell Ruby to "hey make these methods for me."
class Brick
attr_reader :color
attr_writer :color
attr_reader :owner
attr_writer :owner
def initialize(color: "gold", owner: "Me!")
@color = color
@owner = owner
end
end
my_brick = Brick.new(color: "red")
puts my_brick.color
puts my_brick.owner
my_brick.owner = "You?"
puts my_brick.owner
- Make more bricks
- Change the parameter order around
- Write expressions to compare parts of your bricks
^ How did we do?
- How do we determine the volume of a
Brick
?- Length
*
Width*
Height
- Length
^ Make it work! :)
^ The normal mathy notation is l *
w *
h
class Brick
def initialize(l: 5, w: 2, h: 1)
@l = l
@w = w
@h = h
end
def volume
@l * @w * @h
end
end
my_brick = Brick.new
puts my_brick.volume
^ What's wrong with this code? ^ Oh right, these names are awful.
Pick the best variable name
my_favorite_number = 5
fav_num = 5
fav = 5
number = 5
n = 5
mfn = 5
temp = 5
Names should be
- Meaningful
- Easy to read
- Concise
- Specific
- Speling
^ Don't be cute
occupation = "Software Developer"
occupation_name = "Software Developer"
job_title = "Software Developer"
- Code is read more often than it is written
- The next programmer to read your code should
- Understand what you are trying to do
- Even if it doesn't work
- Especially if you are the next programmer
- Huge names are awesome
- If it takes a whole sentence to clearly explain what a variable is for, use it
adjustment_to_be_made_at_time_of_purchase
is perfect
What is the problem we're solving?
- We work for the Brick Store
- They sell Bricks
- We are building a new website for them so that:
- Customers can buy Bricks
- We can manage the inventory of Bricks
^ Ok now that we're committed to making sense when we write code, let's explore some more Ruby things in our Ruby
class Brick
attr_reader :color, :owner
attr_writer :color, :owner
def initialize(color: "gold", owner: "Me!")
@color = color
@owner = owner
end
end
my_brick = Brick.new(color: "red")
puts my_brick.color
puts my_brick.owner
my_brick.owner = "You?"
puts my_brick.owner
class Brick
attr_accessor :color, :owner
def initialize(color: "gold", owner: "Me!")
@color = color
@owner = owner
end
end
my_brick = Brick.new(color: "red")
puts my_brick.color
puts my_brick.owner
my_brick.owner = "You?"
puts my_brick.owner
^ Cool beans, except now we have a subtle thing that's not totally wrong but it's kind of wrong.
What does it really do?
def color
@color
end
def color=(new_color)
@color = new_color
end
- When our brick receives the message:
What is your color?
- We answer with:
This is my color.
^ This should be the only place we return @color
This should be the only place we return
@color
- When our brick receives the message:
I want to change your color to be purple.
- We answer with:
I have changed my to purple.
^ This should be the only place we change @color
This should be the only place we change
@color
class Brick
def initialize(color: "gold")
@color = color
end
def color
@color
end
def color=(new_color)
@color = new_color
end
end
my_brick = Brick.new
puts my_brick.color
my_brick.color = "blue"
puts my_brick.color
The use of color, color, color in
initialize
is tricky.
^ Let's consider this smaller version of Brick
^ We want to make @color = color
in initialize use our color=
method.
^ Before, we learned about using @
to say "this is my attribute value that exists throughout my class"
^ How do we make sure that we're using our own methods?
class Brick
def initialize(initializing_brick_color)
@color = initializing_brick_color
end
def color
@color
end
def color=(new_color)
@color = new_color
end
end
my_brick = Brick.new("gold")
puts my_brick.color
my_brick.color = "blue"
puts my_brick.color
class Brick
def initialize(initializing_brick_color)
self.color = initializing_brick_color
end
def color
@color
end
def color=(new_color)
@color = new_color
end
end
my_brick = Brick.new("gold")
puts my_brick.color
my_brick.color = "blue"
puts my_brick.color
^ The only change here is self.color
, and it puts us in line with how we want to control our use of @color
.
^ Thanks Ken Auer for self encapsulation!
class Brick
def initialize(color: "gold")
self.color = color
end
def color
@color
end
def color=(new_color)
@color = new_color
end
end
my_brick = Brick.new(color: "gold")
puts my_brick.color
my_brick.color = "blue"
puts my_brick.color
^ It's true, named parameters makes your initialize
method more confusing. It is hugely overshadowed by how much more helpful the use of Brick is everywhere else. Look at how clear Brick.new
is now.
^ This is a good time to talk about the flow of control and walk through what happens when we create or update a Brick.
class Brick
def initialize(initializing_brick_color)
puts "Initialize BEGIN with initializing_brick_color '#{initializing_brick_color}'"
self.color = initializing_brick_color
puts "Initialize END with initializing_brick_color '#{initializing_brick_color}'"
end
def color
puts "Reader BEGIN/END 'color' with @color #{@color}"
@color
end
def color=(new_color)
puts "Writer BEGIN 'color=' with @color '#{@color}' and new_color '#{new_color}'"
@color = new_color
puts "Writer END 'color=' with @color '#{@color}' and new_color '#{new_color}'"
end
end
puts "----Program Start----"
puts "1. Make a gold brick."
my_brick = Brick.new("gold")
puts
puts "2. Print the color of my brick."
puts my_brick.color
puts
puts "3. Change the color of my brick to be blue"
my_brick.color = "blue"
puts
puts "4. Print the color of my brick."
puts my_brick.color
puts
puts "----Program Stop----"
- As employees of the Brick Store, we have a new boss.
- His name is Jack
- Jack considers himself to be a master of bricks.
- Jack prefers the blue bricks and will not share.
- Jack wants our website enforce these rules.
The Brick Master
class Brick
attr_accessor :color, :owner
BRICK_MASTER = "Jack"
def initialize(color: "gold", owner:)
self.color = color
self.owner = owner
end
end
my_brick = Brick.new(owner: "Our Local Store")
puts my_brick.owner
my_brick.owner = "Customer"
puts my_brick.owner
What does this do?
class Brick
attr_accessor :color, :owner
BRICK_MASTER = "Jack"
def initialize(color: "gold", owner:)
self.color = color
self.owner = owner
BRICK_MASTER = owner
end
end
my_brick = Brick.new(owner: "Someone")
puts my_brick.owner
my_brick.owner = "You?"
puts my_brick.owner
^ Why? ^ What about Pi?
What does this do?
class Brick
attr_accessor :color, :owner
BRICK_MASTER = "Jack"
def initialize(color: "gold", owner:)
self.color = color
self.owner = owner
end
end
my_brick = Brick.new(owner: "Someone")
puts my_brick
^ I want to be able to print out my brick on the website, and I don't really want to have to ask it about owner
and color
all the time.
^ What if a brick has 10 attributes, or 100?
^ What if I really only care about the 5 of them?
^ What if the "pretty" version of my brick has some rules associated with it?
^ Consider a crazy boss like Jack.
class Brick
attr_accessor :color, :owner
BRICK_MASTER = "Jack"
def initialize(color: "gold", owner:)
self.color = color
self.owner = owner
end
def to_s
"I am a Brick"
end
end
my_brick = Brick.new(owner: "Someone")
puts my_brick
def to_s
end
@color
"#{@color}"
"Color: #{@color}"
"Owner: #{@owner} Color: #{@color}"
"Owner: #{@owner}\nColor: #{@color}"
"Owner: #{@owner}\tColor: #{@color}"
^ String interpolation!
^ \n
^ \t
class Brick
attr_accessor :color, :owner
BRICK_MASTER = "Jack"
def initialize(color: "gold", owner:)
self.color = color
self.owner = owner
end
def to_s
"Owner: #{self.owner}\tColor: #{self.color}"
end
end
my_brick = Brick.new(owner: "Someone")
puts my_brick
def owned_by_the_brick_master?
# ???
end
^ Given Jack's rules, how do we make decisions with ruby code?
true
false
!true
!false
true && true
true && false
true || false
false || true
false && false
false || false
^ In the REPL
Expressions!
1 == 1
1 == 2
1 != "Hello"
"Ruby" == "Ruby?"
1 < 2
1 > 2
1 <= 1
1 <= 2
2 >= 1
2 >= 2
Order of Operations
(1) == (2)
(1 + 1) == 2
(1 + 1) == (2 + 0)
(1 + 1) == (2 + 100)
(1 + 1) <= (2 + 100)
^ Precedence is annoying, so we use ()'s
- Defined vs. Undefined
- Does this thing exist?
- Does it have a value?
puts nil
nil && true
nil || true
false || nil
nil && nil
-
Truthy
true
is truthy Any expression with a defined value is truthy -
Falsey
false
is falseynil
is falsey
- These are all Truthy
"hello"
String
1
Time.new
0
is truthy
^ This is one of the things that is hard for programmers new to Ruby
Which ones are truthy?
true
1
3.14 == 3.14
3.14 * 100 == 3.14 * 10**2
0
0 || false
0 && 0
false && 0
!nil
(!false || nil) && 0
def owned_by_the_brick_master?
@owner == BRICK_MASTER
end
^ This is how we'd really write it
What do we put in
color=
?
class Brick
attr_accessor :owner
attr_reader :color
BRICK_MASTER = "Jack"
def initialize(color: "gold", owner:)
self.owner = owner
self.color = color
end
def color=(new_color)
# What goes here?
end
def owned_by_the_brick_master?
self.owner == BRICK_MASTER
end
def to_s
"Owner: #{self.owner}\tColor: #{self.color}"
end
end
my_brick = Brick.new(owner: "Someone")
puts my_brick
puts my_brick.owned_by_the_brick_master?
Homework
- Ruby Core Library
- What can Strings do?
- Welcome back!
- Variables – Literals, Constants, Instance Variables
- Classes
attr_reader
,attr_writer
,attr_accessor
,to_s
- Initializers, Default Values
- Names
- Named Parameters
- Expressions – Logic,
nil
, Truthy, Falsey
class Brick
attr_accessor :owner
attr_reader :color
BRICK_MASTER = "Jack"
def initialize(color: "gold", owner:)
self.owner = owner
self.color = color
end
def color=(new_color)
@color = new_color
end
def owned_by_the_brick_master?
self.owner == BRICK_MASTER
end
def to_s
"Owner: #{self.owner}\nColor: #{self.color}"
end
end
someones_brick = Brick.new(owner: "Someone", color: "red")
privileged_brick = Brick.new(owner: "Jack", color: "blue")
forbidden_brick = Brick.new(owner: "Someone", color: "blue")
What do we put in
color=
?
## The rest of Brick is the same
def color=(new_color)
# ???
end
## The rest of Brick is the same
Let's state it in english:
- Jack is the Brick Master and he wants to keep all the blue bricks for himself.
- Anyone can buy bricks as long as they are not blue.
- Jack can buy blue bricks.
- If someone other than Jack tries to buy a blue brick, they must be stopped.
def color=(new_color)
- If this new color is not blue then I should change the color.
- If the owner is Jack then I should change the color.
- If this new color is blue and the owner is not Jack then we must not change the color.
end
^ These are the same rules, but I've rewritten them.
def color=(new_color)
- These are the ways we can stay happy
- If this new color is not blue then I should change the color.
- If the owner is Jack then I should change the color.
- This is the way we are not happy
- If this new color is blue and the owner is not Jack then we must not change the color.
end
def color=(new_color)
- If this new color is not blue then I should change the color.
- If the owner is Jack then I should change the color.
end
Let's break this into smaller pieces
- If this
- new color
- is not
- blue
- then I should
- change the color
Now we'll shed some English
- if
- new color
- is not
- blue
- then
- change the color
Now with some more Ruby
def color=(new_color)
- if
new_color
- is not
- blue
- then
@color = new_color
end
How do we write not blue?
Almost all Ruby?
def color=(new_color)
- if
new_color
!=
"blue"
- then
@color = new_color
end
^ So close!
Almost all Ruby?
def color=(new_color)
if
new_color
!=
"blue"
then
@color = new_color
end
^ So close!
Almost all Ruby?
def color=(new_color)
if new_color != "blue"
then
@color = new_color
end
^ Still broken
def color=(new_color)
if new_color != "blue"
@color = new_color
end
end
def color=(color)
- If this new color is not blue then I should change the color.
- If the owner is Jack then I should change the color.
end
Let's break this into smaller pieces
- If the
- owner
- is
- Jack
- then I should
- change the color
Shed some English
- if
- owner
- is
- Jack
- then
- change the color
- end
Add some Ruby
- if
- owner
- is
- Brick Master
- then
@color = new_color
- end
Add more Ruby
- if
- owner
- is
BRICK_MASTER
- then
@color = new_color
- end
Add more Ruby
- if
self.owner == BRICK_MASTER
- then
@color = new_color
- end
^ What is and
?
true and true
true or false
^ These totally work, but they work in a different order than &&
and ||
, so we never ever use them.
Add more Ruby
if self.owner == BRICK_MASTER
@color = new_color
end
def color=(new_color)
if new_color != "blue"
@color = new_color
end
if self.owner == BRICK_MASTER
@color = new_color
end
end
- If this new color is blue and the owner is not Jack then we must not change the color.
if new_color == "blue" && self.owner != BRICK_MASTER
- We must not change the color.
end
^ What would we call our intention here?
^ Bail out, escape, explode, error, complain?
^ fail
if new_color == "blue" && self.owner != BRICK_MASTER
fail "We must not change the color."
end
We're not quite done
def color=(new_color)
if new_color != "blue"
@color = new_color
end
if self.owner == BRICK_MASTER
@color = new_color
end
if new_color == "blue" && self.owner != BRICK_MASTER
fail "We must not change the color."
end
end
def color=(new_color)
if new_color != "blue"
@color = new_color
end
if owned_by_the_brick_master?
@color = new_color
end
if new_color == "blue" && !owned_by_the_brick_master?
fail "We must not change the color."
end
end
def color=(new_color)
if (new_color != "blue") || owned_by_the_brick_master?
@color = new_color
end
if new_color == "blue" && !owned_by_the_brick_master?
fail "We must not change the color."
end
end
def color=(new_color)
if (new_color != "blue") || owned_by_the_brick_master?
@color = new_color
end
if new_color == "blue" && !owned_by_the_brick_master?
fail "We must not change the color."
end
end
^ What does Flow of Control mean and what does it have to do with fail
?
def color=(new_color)
if new_color == "blue" && !owned_by_the_brick_master?
fail "Blue bricks are reserved for #{BRICK_MASTER}."
end
if (new_color != "blue") || owned_by_the_brick_master?
@color = new_color
end
end
^ If we make it past our our pertinent negative, will our next expression in the if
ever be false?
def color=(new_color)
if new_color == "blue" && !owned_by_the_brick_master?
fail "Blue bricks are reserved for #{BRICK_MASTER}."
else
@color = new_color
end
end
^ This is good as is. We can make it a little better.
class Brick
attr_accessor :owner
attr_reader :color
BRICK_MASTER = "Jack"
def initialize(color: "gold", owner:)
self.owner = owner
self.color = color
end
def color=(new_color)
if new_color == "blue" && !owned_by_the_brick_master?
fail "Blue bricks are reserved for #{BRICK_MASTER}."
else
@color = new_color
end
end
def owned_by_the_brick_master?
self.owner == BRICK_MASTER
end
def to_s
"Owner: #{self.owner}\nColor: #{self.color}"
end
end
someones_brick = Brick.new(owner: "Someone", color: "red")
privileged_brick = Brick.new(owner: "Jack", color: "blue")
forbidden_brick = Brick.new(owner: "Someone", color: "blue")
- Welcome back!
- Conditionals
- Happy/Unhappy Paths, Pertinent Negatives
if
(then)else
- Rejecting bad things
fail
- Translating English into Ruby
- Debugging
What is the problem we're solving?
- We work for the Brick Store
- They sell Bricks
- We are building a new website for them where
- We manage the inventory of Bricks
- Customers can buy Bricks
- Jack is our boss and he's keeping the blue bricks for himself
class Brick
attr_accessor :owner
attr_reader :color
BRICK_MASTER = "Jack"
def initialize(color: "gold", owner:)
self.owner = owner
self.color = color
end
def color=(new_color)
if new_color == "blue" && !owned_by_the_brick_master?
fail "Blue bricks are reserved for #{BRICK_MASTER}."
else
@color = new_color
end
end
def owned_by_the_brick_master?
self.owner == BRICK_MASTER
end
def to_s
"Owner: #{self.owner}\nColor: #{self.color}"
end
end
Leave our
class Brick
.....end
code unchanged from now on.
class BrickStore
end
brick_store = BrickStore.new(name: "Ruby's Bricks")
^ Hey this is totally broken! How do we fix it?
class BrickStore
attr_accessor :name
def initialize(name:)
self.name = name
end
end
brick_store = BrickStore.new(name: "Ruby's Bricks")
- Bricks?
- Strict controls on what brick colors are acceptable to society as a whole?
- Authoritarian enforcement of the brick supply chain as it manifests in each and every franchised store?
^ All of the above! :)
In the REPL on the right side:
colors = [ "red", "green", "blue", "yellow", "gray" ]
colors.class
^ What does Array sound like it does? ^ We call objects like this collections.
colors.any?
colors.empty?
colors.size
colors.first
colors.last
colors.sort
colors.reverse
colors.shuffle
colors.sample
colors.include?("blue")
colors.index("yellow")
colors.at(0)
colors.at(2)
colors[3]
colors[99]
colors[1..4]
colors.sort.reverse[-2]
colors.push("brown")
colors << "magenta"
colors << 12345
colors << Time.now
colors << nil
colors.compact
colors.compact!
colors.delete("brown")
colors.delete_at(2)
colors.delete_at(-1)
colors.join("\n")
colors.join(" --- ")
class BrickStore
attr_accessor :name
def initialize(name:)
self.name = name
end
def approved_brick_colors
[ "red", "green", "blue", "yellow", "gray" ]
end
end
brick_store = BrickStore.new(name: "Ruby's Bricks")
brick_store.approved_brick_colors
class BrickStore
attr_accessor :name
def initialize(name:)
self.name = name
end
def approved_brick_colors
[ :red, :green, :blue, :yellow, :gray ]
end
end
brick_store = BrickStore.new(name: "Ruby's Bricks")
brick_store.approved_brick_colors
^ Don't change your BrickStore ^ In Ruby, Symbols are lightweight Strings, and we've been using them already. ^ We use them everywhere for speed, ease of typing, and readability. ^ For our Bricks, it'll be a helpful way to transition a color from being any old String to a concept that has a name.
class BrickStore
attr_accessor :name
attr_reader :inventory
def initialize(name:)
self.name = name
@inventory = []
end
def approved_brick_colors
[ "red", "green", "blue", "yellow", "gray" ]
end
end
brick_store = BrickStore.new(name: "Ruby's Bricks")
brick_store.approved_brick_colors
brick_store.inventory
What is the next part of the problem we can solve?
- We work for the Brick Store
- They sell Bricks
- We are building a new website for them where
- Customers can buy Bricks
- We can manage the inventory of Bricks
- Jack is our boss and he's keeping the blue bricks for himself
How do normal stores handle inventory?
^ The store gets shipments of stuff ^ People buy stuff
- Jack, as our boss, is in charge of receiving all shipments of bricks. When the shipment arrives, he effectively takes ownership of all the bricks until they are sold.
- Each shipment we receive has a total of 10 Bricks that are from a random assortment of acceptable colors.
^ How do we build this?
Jack, as our boss, is in charge of receiving all shipments of bricks. When the shipment arrives, he effectively takes ownership of all the bricks until they are sold.
def one_new_brick
# ???
end
^ Let's make this as a group.
Each shipment we receive has a total of 10 Bricks that are from a random assortment of acceptable colors.
def receive_new_shipment!
# ???
end
^ Let's make this as a group.
- Let's make a report that
- scans our inventory
- reports on the # of bricks for each color
Can we write this using only arrays?
What if we want to "store" the report as we go?
^ Let's make this as a group!
Let's add some more features to our
BrickStore
class Brick
attr_accessor :owner
attr_reader :color
BRICK_MASTER = "Jack"
def initialize(color: "gold", owner:)
self.owner = owner
self.color = color
end
def color=(new_color)
if new_color == "blue" && !owned_by_the_brick_master?
fail "Blue bricks are reserved for #{BRICK_MASTER}."
else
@color = new_color
end
end
def owned_by_the_brick_master?
self.owner == BRICK_MASTER
end
def to_s
"Owner: #{self.owner}\nColor: #{self.color}"
end
end
class BrickStore
attr_accessor :name, :default_delivery_size
attr_reader :inventory
def initialize(name:, default_delivery_size:)
self.name = name
self.default_delivery_size = default_delivery_size
@inventory = []
end
def approved_brick_colors
[ :red, :green, :blue, :yellow, :gray ]
end
def one_new_brick
Brick.new(
owner: Brick::BRICK_MASTER,
color: approved_brick_colors.sample
)
end
def accept_delivery(delivery_size: default_delivery_size)
delivery_size.times do
@inventory << one_new_brick
end
end
def to_s
report = {}
approved_brick_colors.sort.each do |color|
report[color] = 0
inventory.each do |brick|
if brick.color == color
report[color] += 1
end
end
end
report.inspect
end
end
brick_store = BrickStore.new(name: "Ruby's Bricks", default_delivery_size: 5)
brick_store.accept_delivery
brick_store.accept_delivery(delivery_size: 25)
puts brick_store.inventory.size
puts brick_store
^ This is a final BrickStore
that the class, as a group, can work towards.
- There is a lot more to Ruby, programming, and OO.
- You are ready to tackle RubyMonk:
Stuck? Try @chorn.