您的位置:首页 > 编程语言 > Ruby

Instance Variables in ruby

2016-05-01 14:16 471 查看
Dogs have many shared characteristics, like the abilities to wag their tails and drink water from a bowl, but they also have information about them that is variable,

like their breed or their name.

Similarly, when designing an application, your users will have common traits, like the ability to log in and out, but some information will be variable,

like a username or email address. Let’s start by setting up a new
User
class. 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
User
class that can track its own user information.

Say you 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 that error bit by bit.

NoMethodError
: If I had to guess, I’d say this likely has something to do with a method not existing.

undefined method 'username'
: Suspicions confirmed. It looks like our code can’t find a 'username' method. That makes sense, we never defined one.

But you weren't trying to create a new method! You were thinking about this
username
like a variable, right?

Ruby has no way of distinguishing between variables and methods in this case, since they both come after the
.
, so it only supports instance methods.

😱What do we do now?! To treat
username
as 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, most characters are fair game with methods, including
=
. In this case, the
value
argument stands in place of the value you want to assign to
username
.

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 the
julia.username = “coolgirl2000”
we were going for.

Luckily, we can leave off the parentheses since those are optional for methods. These little shortcuts are 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 the
=
is pressed up against
username
is still 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, do something about it.

Syntactic sugar to the rescue again. When a method ends with
=
, Ruby rightfully assumes that you’re creating a method for variable assignment. To make your life sweeter, Ruby lets you put a space before the
=
so it reads more like typical variable assignment, like 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 your
username
. If my math is correct, it should be set to
“coolgirl2002”
right now.

julia.username
# NoMethodError: undefined method `username' for #<User:0x007fc6fa034148>


This error again. You’ve seen this one before, but didn’t 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 this works.

julia.username
# => nil


There’s no error 🎉, but it’s also returning
nil
instead of
“coolgirl2002”
. You may have already caught on to this…

neither your
username
nor your
username=
methods actually do anything.

To make your getter and setter methods start working, you’re going to need to capture the
username
values somewhere.

The problem here has to do with scope. How can the values you capture in the
username=
setter method be accessed in the
username
getter method?

Much like instance methods, you can also use instance variables. Instance variables are variables that are scoped to the entire class.

This means that once an 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 this works before moving on.

julia.username = "coolgirl3000"
# => "coolgirl3000"
julia.username
# => "coolgirl3000"


🙌 Nice!

The
@username
instance variable is now available to use by any of the methods inside of a
User
object.

You can go ahead and make other
User
objects now, and each one will be able to track their own information.

walter = User.new
# => #<User:0x007fc6fa034328>
walter.username = "cooldude17"
# => "cooldude17"
walter.username
# => "cooldude17"


And one last check with Julia to make sure her information is still accurate.

julia.username
# => "coolgirl3000"


Remember when we said that
$global_variables
weren’t the best solution? This is that best solution.

Very rarely will you need a variable that is available to everything inside your application. Often, you just want it available to one or a few objects.

Let me say that again: You will almost never need to use global variables ever again. If you find yourself using one,

there’s a very good chance it can be done some other way using classes.

Getter and setter methods are used so often in Ruby that there are even more shortcuts you can use to make your life sweeter.

Why type more code than you really need to? Remember, Ruby is optimized for developer happiness.

class User
attr_reader :username
attr_writer :username
end


attr_reader
and
attr_writer
(“attr” being shorthand for “attribute”) are two methods that take care of creating getter and setter methods, respectively, for you.

Best of all, you can do this for as many attributes as you’d like. Perhaps you want users to be able to change their usernames, but not their birthdays.

class User
attr_reader :username, :birthday
attr_writer :username
end


There’s no need to actually write out all those getter and setter methods yourself anymore!

One thing that confused me at first was the syntax of
attr_reader
and
attr_writer
.

It didn’t really look like anything I’d ever seen before, at least not until someone rewrote it for me this way:

attr_reader(:username, :birthday)


These are actually instance methods that are already baked in for you to use.

All you are doing is passing in a Symbol representing the instance variable you’d like to create getter and setter methods for.

Accessors

There’s one last refactor we can do here. It’s pretty common to need both a getter and a setter method for an attribute, so why not define both at once?

That’s exactly what
attr_accessor
does.

class User
attr_accessor :username
end


I know what you’re thinking. Yes, I did just string you along this emotional journey only to end up writing three lines of code.

But think about it: now you know what these three lines of code actually do!

Along the way, you learned about instance variables, some neat syntactic sugar, and getters and setters. Not bad.

Let’s continue with the
User
example. This is what we’ve got at the moment:

class User
attr_accessor :username
end


This is useful, but presents a slight issue. We don’t want a user to ever be without a username.

The way things are currently set up, you can easily do something like this:

colt = User.new
# => #<User:0x007fc6fb03ae98>
colt.username
# => nil


Because a
User
is created without a default
username
value, Colt is left without a username until he goes in and sets one himself.

🚫This will not do.

There’s a special instance method you can use to make sure an object is properly set up when it’s created,
initialize
.

When you create a new object, the first thing Ruby will do is look for that
initialize
method and run it.

This makes is 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
“its_colt_outside”
to be the default username for every single user.

That’s not going to help you much when Orit wants to make an account for herself.

How about passing in the username as an argument right from the start, like a method, when creating the new
User
object?

orit = User.new("supermom")


To do this, you’ll need to modify your
User
class so that it can accept arguments. It may be tempting to do something like this:

class User(username)
# ...
end


But as you’ll quickly encounter, this will throw an error. This is a completely reasonable assumption, though.

Instead, you can include the argument as a part of the
initialize
method.

A good way to remember this is to keep in mind that
initialize
takes care of everything related to the initial set-up of an object.

Since the
username
argument deals with the initial set-up of a username, it belongs to the
initialize
method.

class User
attr_accessor :username

def initialize(username)
@username = username
end
end


Now you can initialize a
User
object with a username right when you create it.

orit = User.new("supermom")
# => #<User:0x007fc6fb03ae98 @username="supermom">
orit.username
# => "supermom"


This also safeguards you 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 be too tough to figure out what the issue is.

ArgumentError
: This probably has something to do with the arguments passed into this
User
object.

wrong number of arguments (0 for 1)
: Zero arguments were passed in, when the
User
class is expecting one.

Errors can seem scary, but often they contain vital information to help fix bugs in your code. Good error messages will tell you exactly where you need to make a fix.
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: