Tài liệu hạn chế xem trước, để xem đầy đủ mời bạn chọn Tải xuống
1
/ 55 trang
THÔNG TIN TÀI LIỆU
Thông tin cơ bản
Định dạng
Số trang
55
Dung lượng
283,84 KB
Nội dung
176 CHAPTER 6 Modules and program organization When you create your application, there’s nothing in the models directory; you create all the models with the generate script. However, you get a free controller file: the generic file app/controllers/application.rb , which serves as an umbrella controller file for all the other controllers. Upon the automatic creation of this file, you see something similar to what you saw in the newly minted model files: class ApplicationController < ActionController::Base end This code creates a class (or perhaps reopens a class; you can’t tell by looking, although in this case the action is creation) called ApplicationController , which is a subclass of a class called Base that is nested inside a class or module (module, as it happens) called ActionController . The new class created here ends up serv- ing as the superclass for the other controller classes you create later with the gen- erate script—as you can see if you look in one of the application-specific controller files: class ComposerController < ApplicationController We’ll come back and flesh out the ramifications of this discussion, particularly in part 4 when we revisit the music store application and bear down on further details of coding inside the model and controller files. Meanwhile, you now have a good sense of the centrality of modules as well as classes (which, again, are a spe- cialized form of module) to Ruby programming in general and the Rails frame- work specifically. 6.4 Summary This chapter has been both a companion to and a continuation of the previous chapter on classes. We’ve looked in detail at modules, which are similar to classes in that they bundle methods and constants together, but which can’t be instanti- ated. You’ve seen examples of how you might use modules to express the design of a program. We’ve taken an object’s-eye view of the process of finding and exe- cuting a method in response to a message. We’ve also looked at some techniques you can use—including nesting classes and modules inside each other, which can have the benefit of keeping namespaces separate and clear. Finally, we discussed aspects of modular organization in the Rails framework source and in some of the boilerplate code created by Rails when you initialize your application. Now that we’re nesting elements inside each other, the next topic we should and will examine in detail is scope: what happens to data and variables when your program moves from one code context to another. 177 The default object (self) and scope In this chapter ■ The role of the current or default object, self ■ Scoping rules for variables and constants ■ Method access rules 178 CHAPTER 7 The default object (self) and scope In describing and discussing computer programs, we often use spatial and, some- times, human metaphors. We talk about being in a class definition, or returning from a method call. Sometimes there’s a sense of addressing objects in the second per- son, as in obj.respond_to?("x") (that is, “Hey obj , do you respond to ‘ x ’?”). As your program runs, the context and orientation change again and again. This chapter is about knowing what’s going on in a Ruby program, based on understanding what different elements mean, and why, in certain contexts. A few components mean the same thing everywhere. Integers, for example, mean what they mean wherever you see them. The same is true for keywords: You can’t use keywords like def and class as variable names, so when you see them, you can easily glean what they’re doing. But most elements depend on context for their meaning. Most words and tokens can mean different things at different times. If you understand what can change from one context to another, and also what triggers a change in context (for example, starting a method definition), you can always get your bearings in a Ruby program. And it’s not just a matter of passive Ruby literacy: You also need to know about contexts and how they affect the meaning of what you’re doing when you’re writing Ruby. This chapter focuses primarily on two topics: scope and self. As we discussed briefly a little earlier, the rules of scope govern the visibility of variables (and other elements, but largely variables). It’s important to know what scope you’re in, so that you can tell what the variables refer to and not confuse them with variables from different scopes that have the same name. Unlike scope, self isn’t so much a concept as an object. However, self changes in the course of program. At every moment, only one object is playing the role of self. But it’s not necessarily the same object from one moment to the next. Self is like the first person or I of the program. As in a book with multiple first-person narrators, the I role can get passed around. There’s always one I, but who it is— what object it is—will vary. Both of these components of Ruby pertain directly and centrally to the matter of staying correctly oriented in a program. In order to know what you’re looking at, you need to know what scope you’re in. And in order to understand what the things you’re looking at do, you need to know which object is self. The third subtopic of this chapter is method access. Ruby provides mechanisms for making distinctions among access levels of methods. Basically, this means rules limiting the calling of methods depending on what self is. Method access is there- fore a meta-topic, grounded in the study of self and scope. We’ll look at Ruby’s Understanding self, the current/default object 179 method-access rules both as a general matter and in their role as a mechanism for creating layers of access to Rails controller actions. Finally, we’ll discuss a topic that pulls together several of these threads: top- level methods, which are written outside of any class or module definition. 7.1 Understanding self, the current/default object One of the cornerstones of Ruby programming—the backbone, in some respects—is the default object or current object, accessible to you in your program through the keyword self . At every point when your program is running, there is one and only one self. Being self has certain privileges, as you’ll see. In this sec- tion, we’ll look at how Ruby determines which object is self at a given point and what privileges are granted to the object that is self. 7.1.1 Who gets to be self, and where There is always one (and only one) current object or self. You can tell which object it is by following a small set of rules. These rules are summarized in table 7.1; the table’s contents will be explained and illustrated as we go along. To know which object is self, you need to know what context you’re in. In prac- tice, there aren’t all that many contexts to worry about. There’s the top level (before you’ve entered any other context, such as a class definition). There are class definition blocks, module definition blocks, and method definition blocks. Aside from a few subtleties in the way these contexts interact, that’s about it. As shown in table 7.1, self is determined by which of these contexts you’re in (class and module definitions are similar and closely related). Figure 7.1 gives you a diagrammatic summary of the information from table 7.1. Both show you that some object is always self, and that which object is self depends on where you are in the program. The most basic and, in some respects, unique program context is the top level, the context of the program before any class or module definition has been opened, or after they’ve all been closed. We’ll look next at the top level’s ideas about self. 180 CHAPTER 7 The default object (self) and scope Table 7.1 How the current object (self) is determined Context Example Which object is self? Top level of program Any code outside of other blocks main (built-in top-level default object) Class definition class C The class object C Module definition module M The module object M Method definitions 1. Top level ddd def method_name main (built-in top-level default object) 2. Instance method definition ddd class C ddd dddef method_name An instance of C , responding to method_name 3. Instance method definition in module ddd module M ddd dddef method_name I. Individual object extended by M II. Instance of class that mixes in M 4. Singleton method (including class ddmethods) dddef obj.method_name obj self at top level is the "default default object," main self inside a class definition is the class object itself self inside any method is the object to which the message (the method call) was sent for a class method, that means the class object for an instance method, that means an instance of the class whose instance method it is puts "Top Level" puts "self is # {self}" class C end def m puts "Class definition block:" puts "self is # {self}" def self.x puts "Class method C.x:" puts "self is # {self}" puts "Instance method C # x:" puts "self is # {self}" end end Figure 7.1 Diagrammatic view of the determination of self in different contexts Understanding self, the current/default object 181 The top-level self object The term top level refers to program code written outside of any class or module definition block. If you open a brand-new text file and type x = 1 you have created a top-level local variable x . If you type def m end you have created a top-level method—a method that isn’t defined as an instance method of a particular class or module nor associated uniquely with an individual object (it isn’t a singleton method). A number of our examples, particularly in the early chapters, involved top-level code. Once we started writing class and module definitions, more of our code began to appear inside those definitions. The way self shifts in class, module, and method definitions is uniform: The keyword ( class , module , or def ) marks a switch to a new self. But what is self when you haven’t yet entered any definition block? The answer is that Ruby provides you with a start-up self at the top level. If you ask it to identify itself ruby -e 'puts self' it will tell you that it’s called main . main is a special term the default self object uses to refer to itself. You can’t refer to it as main . If you want to grab main for any reason, you need to assign it to a variable at the top level: m = main (It’s not likely that you’d need to do this, but this is how it’s done.) Self inside class and module definitions In a class or module definition, self is the class or module object. This innocent- sounding rule is important. If you master it, you’ll save yourself from several of the most common mistakes that people make when they’re learning Ruby. You can see what self is at various levels of class and/or module definition by using puts explicitly, as shown in listing 7.1. 182 CHAPTER 7 The default object (self) and scope class C puts "Just started class C:" puts self module M puts "Nested module C::M:" puts self end puts "Back in the outer level of C:" puts self end As soon as you cross a class or module keyword boundary, the class or module whose definition block you’ve entered—the Class or Module object—becomes self. Listing 7.1 shows two cases: entering C , and then entering C::M . When you leave C::M but are still in C , self is once again C . Of course, class and module definition blocks do more than just begin and end. They also contain method definitions; and method definitions, for both instance methods and class methods, have rules determining self. The determination of self in instance method definitions The notion of self inside an instance method definition is subtle, for the following reason: When the interpreter encounters a def / end block, it defines the method immediately; but the code inside the method definition isn’t executed until later, when an object capable of triggering its execution receives the appropriate message. When you’re looking at a method definition on paper or on the screen, you can only know in principle that, when the method is called, self will be the object that called it (the receiver of the message). At the time the method definition is executed, the most you can say is that self inside this method will be some future object that has access to this method. You can rig a method to show you its self as it runs: class C def x puts "Class C, method x:" puts self end end c = C.new c.x Listing 7.1 Examining self via calls to puts in class and module definitions Output: C Output: C::M Output: C Understanding self, the current/default object 183 which outputs: Class C, method x: #<C:0xbf4c294c> The weird-looking item in the output ( #<C:0xbf4c294c> ) is Ruby’s way of saying “an instance of C .” (The hexadecimal number after the colon is a memory loca- tion reference. When you run the code on your system, you’ll probably get a dif- ferent number.) As you can see, the object we created ( obj ) takes on the role of self during execution of the method x . Self in singleton-method and class-method definitions Instance methods are made to be shared. But singleton methods—those attached to a particular object, like the method talk in def obj.talk —can be called by only one object. As you might expect, when a singleton method is executed, self is the object that owns the method, as an object will readily tell you: obj = Object.new def obj.show_me ddprint "I'm an object; " ddputs "here's self inside a singleton method of mine:" ddp self end obj.show_me print "And inspecting obj from outside, " puts "to be sure it's the same object:" p obj The output of this example is as follows: I'm an object; here's self inside a singleton method of mine: #<Object:0x40193d40> And inspecting obj from outside, to be sure it's the same object: #<Object:0x40193d40> (As always, the exact hexadecimal number in the object’s inspection string will probably be different on your run of the code.) It makes sense that if a method is written to be called by only one object, that object gets to be self. Moreover, this is a good time to remember class methods— defined as singleton methods for class objects. The following example reports on self from inside a class method of C : class C dddef C.x ddddputs "Class method of class C" 184 CHAPTER 7 The default object (self) and scope ddddp self ddend end C.x Here’s what it reports: Class method of class C C Sure enough, self inside a singleton method (a class method, in this case) is the object whose singleton method it is. By way of a little programming tip, here’s a variation on the last example: class C dddef self.x ddddputs "Class method of class C" ddddp self ddend end Note the use of self.x #1 rather than C.x . This way of writing a class method takes advantage of the fact that in the class definition, self is C . So, def self.x is the same as def C.x . The self.x version offers a slight advantage: If you ever decide to rename the class, self.x will adjust automatically to the new name. If you hard- code C.x , you’ll have to change C to your class’s new name. Being self at a given point in the program comes with some privileges. The chief privilege enjoyed by self is that of serving as the default receiver of messages, as you’ll see next. 7.1.2 Self as default receiver of messages Calling methods (that is, sending messages to objects) usually involves the dot notation: obj.talk ticket.venue "abc".capitalize That’s the normal, full form of the method-calling syntax in Ruby. However, a spe- cial rule governs method calls: If the receiver of the message is self, you can omit the receiver and the dot. Ruby will use self as the default receiver, meaning the message you send will be sent to self, as the following equivalencies show: talk venue capitalize B Same as self.talk Same as self.venue Same as self.capitalize B Understanding self, the current/default object 185 NOTE GIVING METHODS AND VARIABLES THE SAME NAMES You can (but really shouldn’t) give a method and a variable the same name. If both exist, and you use the bare identifier (like talk ), the variable takes prece- dence. To force Ruby to see the identifier as a method name, you’d have to use self.talk or call the method with an empty argument list: talk() . Because variables don’t take arguments, the parentheses estab- lish that you mean the method rather than the variable. Again, it’s best to avoid these name clashes if you can. Let’s see this concept in action by inducing a situation where we know what self is and then testing the dot-less form of method calling. In the top level of a class defi- nition block, self is the class object. And we know how to add methods directly to class objects. So, we have the ingredients to do a default receiver demo: class C dddef C.no_dot ddddputs "As long as self is C, you can call this method with no dot" ddend ddno_dot end C.no_dot The first call to no_dot #1 doesn’t have an explicit receiver; it’s a bareword. When Ruby sees this (and determines that it’s a method call, rather than a variable or keyword), it figures that you mean it as shorthand for self.no_dot In the case of our example, self.no_dot is the same as C.no_dot , because we’re inside C ’s definition block and, therefore, self is C . The result is that the method C.no_dot is called, and we see the output. The second time we call the method #2, we’re back outside the class definition block. C is no longer self. Therefore, to call no_dot , we need to specify the receiver: C . The most common use of the dotless method call occurs when you’re calling one instance method from another. Here’s an example: class C dddef x ddddputs "This is method 'x'" ddend dddef y ddddputs "This is method 'y', about to call x without a dot." B C B C [...]... global variables The Ruby interpreter starts up with a fairly large number of global variables already initialized These variables store information that’s of potential use anywhere and everywhere in your program For example, the global variable $0 contains the name of the file Ruby is executing The global $: (dollar sign followed by a colon) contains the directories that make up the path Ruby searches when... load an external file $$ contains the process id of the Ruby process And there are more TIP A good place to see descriptions of all the built-in global variables you’re likely to need— and then some—is the file English.rb in your Ruby installation This file provides less cryptic names for the notoriously cryptic global variable set (Don’t blame Ruby for the names—most of them come from shell languages... instead of $=, $PID instead of $$, and so forth LOOK AT English.rb FOR GLOBAL VARIABLE DESCRIPTIONS The pros and cons of global variables Global variables are tempting for beginning programmers and people learning a new language (not just Ruby, either) They appear to solve lots of design problems: You don’t have to worry about scope, and multiple classes can share information by stashing it in globals rather... The reasons for avoiding them are similar to the reasons they are tempting Using global variables tends to end up being a substitute for solid, flexible program design, rather than contributing to it One of the main points of object-oriented programming is that data and actions are encapsulated in objects You’re supposed to have to query objects for information and to request that they perform actions... printout of the number 1 Forcing an absolute constant path Sometimes you don’t want a relative path Sometimes you really want to start the constant lookup process at the top level—just as you sometimes need to use an absolute path for a file This may happen if you create a class or module with a name that’s similar to the name of a Ruby built-in class or module For example, Ruby comes with a String... object as someone you ask to perform a task for you Let’s say you ask someone to bake you a cake In the course of baking you a cake, the baker will presumably perform a lot of small tasks: measure sugar, crack an egg, stir batter, and so forth The baker can do, and does do, all these things But not all of them have equal status when it comes to what the baker is willing to do for other people, like you... access protection Rails applications provide a great example of a place you may want to use private methods A Rails controller object has a lot in common with a baker Just as a baker has to know how to break eggs but doesn’t field direct requests for breaking eggs, so a controller (in some cases) has to know how to do things for which it doesn’t field direct requests Here’s an example from a Rails- based... exit your program, and so on) with Ruby s top-level methods If you want to see all of them, try this: $ ruby -e 'print Kernel.private_instance_methods.sort' You can add to the mix by writing your own top-level methods Summary 2 05 7 .5 Summary This chapter covered several important topics pertaining to the art of understanding exactly what’s going on at a given point in a Ruby program We talked about the... language feature that figures prominently in Ruby Finally, we’ll look at Ruby s extensive mechanism for handling error conditions through exceptions Exceptions stop the flow of a program, either completely or until the error condition has been dealt with Exceptions are objects, and you can create your own exception classes, inheriting from the ones built in to Ruby, for specialized handling of error conditions... requests Here’s an example from a Rails- based site: http://www.rcrchive.net, the official site for Ruby Change Requests (RCRs) When you sign up for a new user account on RCRchive (which you’re welcome to do, by the way, if you’re interested in following the progress of discussions about possible changes to Ruby, or suggesting changes), you first connect to http://www.rcrchive.net/user/register, which . some—is the file English.rb in your Ruby installation. This file provides less cryptic names for the notoriously cryptic global variable set. (Don’t blame Ruby for the names—most of them come from. triggers a change in context (for example, starting a method definition), you can always get your bearings in a Ruby program. And it’s not just a matter of passive Ruby literacy: You also need. normal, full form of the method-calling syntax in Ruby. However, a spe- cial rule governs method calls: If the receiver of the message is self, you can omit the receiver and the dot. Ruby will use