Dogs has many shared characteristics, like the abilities to wag their tails and drink water from a bowl, but they also ha ve information about them it is variable,
Like their breed or their name.
Similarly, when designing an application, your users would have common traits, like the ability-log in and out, but some Information'll be variable,
Like a username or email address. Let's start by setting up a newUserclass. You can follow along in either IRB or by running a script from the command line.
class User
# stuff will go here
end
By the end of this lesson, you'll be able to define a class so can track itsUserown user information.
Say wanted to assign a user ' s username. You might try something like this.
julia = User.new
julia.username = "coolgirl2000"
# NoMethodError: undefined method `username‘ for #<User:0x007fc6fa034148>
Let's pause to take a look at the error bit by bit.
- NoMethodError: If I had to guess, I ' d say this likely have something to does with a method not existing.
- undefined method ‘username‘: Suspicions confirmed. It looks like our code can ' t find a ' username ' method. That's makes sense, we never defined one.
But you weren ' t trying to create a new method! You were thinking on this likeusernamea variable, right?
Ruby has no how to distinguishing between variables and methods in this case, since they both come after.the, so it only supports instance methods.
?? What does we do now?! To treatusernameas a variable, you ' re going to need to fake this a bit. Let's write just enough code to stop this pesky error.
class User
def username=(value)
end
end
If you recall, the most characters is fair game with methods, including=. In this case, the argument stands in place of the value of yourvaluewant to assignusername.
julia.username=("coolgirl2000")
# => "coolgirl2000"
That does the trick ... sort of. There ' s an awful lot of syntax here, and it's a bit of a stretch to say this looks anything like thejulia.username = “coolgirl2000”we were going For.
Luckily, we can leave off the parentheses since those is optional for methods. These little shortcuts is known as syntactic sugar, since they make writing code with it just a bit sweeter??. Let's see how that looks.
julia.username= "coolgirl2001"
# => "coolgirl2001"
Wow, I already feel so much less stressed looking at that. The fact that's=pressed up against ISusernamestill bothering me. I can ' t stop looking at it.
It's going to bother me forever if you don ' t do something about it. Please, does something about it.
Syntactic sugar to the rescue again. When the A method ends=with, the Ruby rightfully assumes that your ' re creating a method for variable assignment. To make your life sweeter, Ruby lets your put a space before=the so it reads more like typical variable assignment, l IKE so:
julia.username = "coolgirl2002"
# => "coolgirl2002"
?? Boom. Check that out. I can sleep easily again. You ' ve just successfully created (the start of) Asetter method (sometimes called a mutator method).
A setter method is one which sets, or changes, the value of a variable belonging to a particular object.
Alright, let's check in on yourusername. If My math is correct, it should are set to right now“coolgirl2002”.
julia.username
# NoMethodError: undefined method `username‘ for #<User:0x007fc6fa034148>
This error again. You've seen this one before, but didn ' t do you already create this instance method?
Let's go ahead and write the bare minimum amount of code to make this error go away.
class User
def username=(value)
end
def username
end
end
This is the opposite of a setter method, a getter method. It exists to get the value of a variable belonging to an object. Let's see if the This works.
julia.username
# => nil
There's no error??, but it ' s also returningnilinstead of“coolgirl2002”. Already caught on ...
Neither yourusernamenor yourusername=methods actually do anything.
To make your getters and setter methods start working, you ' re going to need to capture theusernamevalues somewhere.
The problem here have to does with scope. How can the values of you capture in theusername=setter method is accessed in theusernamegetter method?
Much like instance methods, you can also use instance variables. Instance variables is variables that is scoped to the entire class.
This means a instance variable is defined, it can be used anywhere in that object.
An instance variable looks just like a regular variable, with one minor difference:it starts with@.
class User
def username=(value)
@username = value
end
def username
@username
end
end
Let's see if the this works before moving on.
julia.username = "coolgirl3000"
# => "coolgirl3000"
julia.username
# => "coolgirl3000"
?? nice!
The instance variable is now available to use by any of the@usernamemethods inside of aUserobject.
You can go ahead and make otherUserobjects now, and each one is able to track their own information.
walter = User.new
# => #<User:0x007fc6fa034328>
walter.username = "cooldude17"
# => "cooldude17"
walter.username
# => "cooldude17"
And one last check with the Julia to make sure she information is still accurate.
Julia.username # = "coolgirl3000"
Remember when we said this$global_variablesweren ' t the best solution? This is the best solution.
Very rarely'll you need a variable that's available to everything inside your application. Often, just want it available to one or a few objects.
Let me say that again:you would almost never need to use global variables ever again. If you find yourself using one,
There ' s a very good chance it can be do some other-the-using classes.
Getter and Setter methods is used so often in Ruby that there is even more shortcuts you can use for make your life sweet Er.
Why type more code than do really need to? Remember, Ruby is optimized for developer happiness.
class User
attr_reader :username
attr_writer :username
end
attr_readerattr_writerand ("attr" being shorthand for "attribute") is both methods that take care of creating getter and setter methods, R Espectively, for you.
Best of all, you can does this for as many attributes as you ' d. Perhaps you want the users to is able to the change their usernames, and not their birthdays.
class User
attr_reader :username, :birthday
attr_writer :username
end
There's no need to actually write off all those getter and setter methods yourself anymore!
One thing that confused me at first is the syntax ofattr_readerandattr_writer.
It didn ' t really look like anything I ' d ever seen before, at least don't until someone rewrote it for me this is:
Attr_reader (: Username,: birthday)
These is actually instance methods that is already baked in for the use.
All your is doing be passing in a Symbol representing the instance variable your ' d like to create getter and setter methods For.
Accessors
There ' s one last refactor we can does here. It's pretty common to need both a getters and a setter method for an attribute, so why isn't define both at once?
That's exactly whatattr_accessordoes.
class User
attr_accessor :username
end
I know what do you ' re thinking. Yes, I did just string "along this emotional journey" only to end up writing three lines of code.
But think about it:now your know what these three lines of code actually do!
Along the learned about instance variables, some neat syntactic sugar, and getters and setters. Not bad.
Let's continue with theUserexample. This is what we have got at the moment:
class User
attr_accessor :username
end
This was useful, but presents a slight issue. We don ' t want a user to ever is without a username.
The things is currently set up and you can easily does something like this:
colt = User.new
# => #<User:0x007fc6fb03ae98>
colt.username
# => nil
Because aUseris created without a defaultusernamevalue, Colt was left without a username until he goes in and sets one hi Mself.
?? This won't do.
There ' s a special instance method you can use the make sure an object is properly set up when it's created,initialize.
When you create a new object, the first thing Ruby would do are look for, andinitializerun it.
This makes are super handy for initial setup (for things like ... initializing an object with a username, maybe?).
class User
attr_accessor :username
def initialize
@username = "its_colt_outside"
end
end
Let's give this another go.
colt = User.new
# => #<User:0x007fc6fb03ae98 @username="its_colt_outside">
colt.username
# => "its_colt_outside"
?? Beautiful.
There ' s just one issue with the previous Example-we ' re hard-coding to being the“its_colt_outside”default username for every single user .
That's not going-to-help if the Orit wants to make a account for herself.
How about passing in the username as a argument right from the start, like a method, when creating the newUserobject?
Orit = User.new ("supermom")
To does this, you'll need to modify your class so thatUserit can accept arguments. It May is tempting to does something like this:
class User (username) # ... End
But as you'll quickly encounter, this would throw an error. This is a completely reasonable assumption, though.
Instead, you can include the argument as a part of theinitializemethod.
A Good A-remember-to-keep-in-mind-takes care ofinitializeeverything related to the initial set-up of an obj Ect.
SinceusernameThe argument deals with the initial set-up of a username, it belongs to theinitializemethod.
class User
attr_accessor :username
def initialize(username)
@username = username
end
end
Now the can initialize aUserobject with a username right when you create it.
orit = User.new("supermom")
# => #<User:0x007fc6fb03ae98 @username="supermom">
orit.username
# => "supermom"
This also safeguards is from creating a new user without a username.
mike = User.new
# ArgumentError: wrong number of arguments (0 for 1)
If you break apart that error, it shouldn ' t being too tough to figure out what's the issue is.
- ArgumentError: This probably have something to does with the arguments passed into thisUserobject.
- wrong number of arguments (0 for 1): Zero arguments were passed in, if theUserclass is expecting one.
Errors can seem scary, but often they contain vital information to help fix bugs in your code. Good error messages would tell you that exactly where you are need to make a fix.
Instance Variables in Ruby