Back to description
W. Web knew immediately that something was wrong. He had suffered stomach pangs before, but never like this. Stumbling out... more
W. Web knew immediately that something was wrong. He had suffered stomach pangs before, but never like this. Stumbling out of the taxi cab and toward the hospital, he mopped the sweat from his brow and pushed his way through the sidewalk traffic.
Inside, everything was a dizzy blur flowing past him nurses, patients, a police officer, and several computer technicians hitting a computer monitor and mumbling something about the Internet going down.
"I know, I know!" Web thought as he struggled past them for the emergency patient entrance.
Luckily for W. Web, this particular hospital uses a triage system, and when you explain to the nurse at the front desk that you are the Internet, you get bumped to the front of the line. A lot is riding on your health.
As Web lay in his hospital gurney, passing other familiar technologies as the nurse pushed him down the hall, he realized that he had made the right decision to stop ignoring the pangs. It was going to be okay.
This book will make you a better web application developer. And if some of the pundits with crystal balls are to be believed, we're all on the path to becoming web application developers. More specifically, this book will make you a better Ruby on Rails developer. It assumes that you have written code using Ruby on Rails and now you are thirsty to understand how to design with Ruby on Rails and how to master the elements of Ruby that make it so successful.
The web is a strange medium for application development. Web applications can't run by themselves, they have little access to a machine's hardware and disk capabilities, and they require a menagerie of client and server software providing them with life support. Despite all this, as you probably already know or are beginning to learn, the Web is a wonderful and exciting place to be developing applications.
Programming for the Web is a blend of art and engineering. Its odd quirks and demands can be harnessed to create applications out of clean, concise, elegant code with minimal waste. This book will show you how.
Programming for the Web is also a task in which good design skills can be the most critical part of a project because of the lack of features such as compilation and type checking found in the desktop world.
Web applications aren’t programs; they are ecosystems. For each separate task, a separate language is called upon: SQL for data persistence; Ruby and others for application logic; HTML for UI structure; CSS for UI appearance; and JavaScript for UI logic. Good design skills must extend past the knowledge of each individual area and incorporate the ability to coordinate all areas. On top of all that, the rise of web APIs and RESTful endpoints enable yet another ecosystem of web applications to communicate with each other and exchange services, adding another layer of abstraction that is built upon the ones below.
The Web is here to stay, and its potential will only grow as a development platform. As Internet access approaches utility-like status, as telephone and television did before it, the distinction between your hard drive and “the cloud” will blur to the point that the two are functionally indistinguishable. With the exception of maybe games and media editors, applications on the Web will be every bit as powerful as those once deployed on CDs and DVDs, but these new applications will be easier to code, faster to deploy, and will harness the combined intelligence of swarms of users to enrich their experience.
These changes are already taking place. In 2007, the primary process for both the Democratic and Republican parties included a presidential debate with a new twist: Questions for the candidates were asked via YouTube videos submitted by ordinary people through the Web. Web front ends for our banks, stocks, and bills are now considered requirements instead of features. It is no longer surprising to store data that we own, such as our documents and photos, to web applications such as Google Docs and Flickr space leased for free in exchange for a bit of advertising. The Web is no longer just about fetching documents; instead, it has become a universal communications medium for both humans and software.
If you are here reading this page, then you see these changes taking place. The question that remains is how to best understand and take advantage of this new development medium.
This book aims to be a blend of design and programming. It takes a critical look at what makes the modern Web tick from the standpoint of Ruby on Rails. The chapters touch on a wide range of topics, from REST-based web design to domain-specific languages and behavior-driven development. All these topics represent the cutting edge of thought about web development and will become cornerstones of the web of applications that will flourish over the coming years.
At times throughout the book, the code will be sparse; elsewhere, it will be frequent. In all chapters, long code examples will be avoided in favor of small code examples interwoven with the text to demonstrate an idea. This is a book primarily about concepts, not syntax.
... less
W. Web approached the picnic tables set up for breakfast cautiously, clutching the orientation binder in his hands. He waved... more
W. Web approached the picnic tables set up for breakfast cautiously, clutching the orientation binder in his hands. He waved as he saw Vik, the man he had met preaching on the street outside the hospital after being discharged. Here in the camp outside the city, all the cult members he remembered from the recruiting sessions were much more relaxed, their nervous edge gone.
Web grabbed a paper plate from the serving line and began perusing the platters, deciding what to eat.
"Excuse me; I'm new here. Do you know if there is a serving spoon for the eggs?" he asked the woman in front of him.
She turned, amused, and smiled with a quizzical brow, "Serving spoon? No, just imply to the platter that you'd like some."
"Imply to the . . I'm sorry, what?"
The woman held her empty plate out to the platter and looked expectantly at it. W. Web couldn't tell whether he had blinked while she pulled a fast one or he just hadn't noticed the eggs on her plate all along, but when she pulled the plate back, it was full.
"We always felt serving spoons were redundant, anyway," she said with a smile. "After all, what else were you going to do with them?"
Web inched his plate toward the inanimate platter, looking a bit confused. He jerked his hands back as an omelet suddenly weighed down the plate.
"There you go! A natural. If you want scrambled, try not to look so confused. And smile for sunny side up."
Web offered a timid smile back and muttered his thanks, shuffling away to clear his head. He slid past the filling picnic tables and back up the hill toward his cabin, the woman's voice wafting behind him.
"If you're headed back to your bunk, make sure you touch the door with your left hand first, otherwise the door won't unlock!" and then after a pause, "Don't worry! It will all start making sense!"
If Rails were a mixed drink, the recipe for it might read, “1 part framework, 1 part language, 2 parts mindset.” Rails is as much a set of guidelines for thinking about web development as it is a framework for using web development. The two can't really be separated. Everything from the Rails project structure to the layout of your HTML files is affected by the Rails mindset, driven by one opinion of how web development should work and understanding that design philosophy can be as important as the mechanical task of writing Ruby and HTML code.
This chapter is a concise review of these components to Rails: the framework, the language, and the mindset. Rails code looks and feels different from non-Rails code, but when you become used to its look, the code you write will be far more productive. This chapter briefly covers these aspects of Rails to review topics such as project structure and how the Rails pipeline operates. Later in the book, you will learn how to extend this architecture with plug-ins and new language-like features. After discussing Rails as a framework and a language, this chapter delves into several vignettes that describe Rails as a state of mind, a style of programming driven by beliefs about how effective and enjoyable programming should work. All these ideas affect the way in which the Rails framework operates and should motivate the design choices you make in your own code. Many of them are also the topics of later chapters in this book.
In many of the areas discussed below, the Rails concept extends past Ruby on Rails itself and to many of the other Rails-like frameworks available. Today, Rails developers can choose from a number of reinterpretations of the basic Rails idea, or they can start from scratch and pick and choose the ideas in Rails that make sense for their own application:
Merb for Ruby (www.merbivore.com)
www.merbivore.com
TurboGears for Python (www.turbogears.org)
www.turbogears.org
Django for Python (www.djangoproject.com)
www.djangoproject.com
CodeIgniter for PHP (www.codeigniter.com)
www.codeigniter.com
CakePHP for PHP (www.cakephp.org)
www.cakephp.org
Each of these frameworks looks and feels different to the programmer, but are all striving toward a set of similar goals. These shared beliefs and goals are important because they represent a new branch of thought about web development. Few of the ideas in the Rails mindset are inventions of Rails. Many are broader movements in the web community or traditional programming ideas reinterpreted in the context of the Web things such as test-driven development, REST-based design, and MVC. But Rails is unique in collecting all these ideas into a unified framework and so thoroughly and seamlessly integrating them into the brick and mortar of web development.
This chapter does not go into deep detail of how to set up a Rails application or how to use the many features the Rails libraries offer you. Several books address these concerns already, among which two of the best are Agile Web Development with Rails, by Dave Thomas and David Heinemeier Hansson, and The Rails Way, by Obie Fernandez. These books are a definite must for the bookshelf of any Rails developer and are valuable references regardless of your skill level. Coupled with one of those or a similar book, this chapter will provide you with a better understanding of the design choices that accompany the Rails framework.
W. Web boarded a bus heading into the city from the Rails compound. It had been a long orientation week. He wasn't sure whether... more
W. Web boarded a bus heading into the city from the Rails compound. It had been a long orientation week. He wasn't sure whether he was ready to shave his head as the rest of them had, but he liked a lot of what he had heard.
Web was on his first trip for the group a rally down in the Big City. It would be interesting, for sure. He squeezed through the crowd to find a seat near the middle, right next to a tall man with a buzz cut who happened to be named Rick.
Rick was riding the bus home from work. He had the reverse commute, living in the city but working out in the country. It was an unusually crowded day for a Tuesday people of all destinations packed together, muffled music seeping from earphones, blank stares emanating from tired faces. Rick inched sideways to create more room as a man wearing a papier-mâché globe around his body struggled to fit into the center-facing seat next to him.
As the bus pulled away from its next stop on the city's edge, one last passenger, dirty and tattered, pushed his way onto the bus.
“Move it!” he shouted gruffly as he pushed a small boy and his mother out of the way, the smell of liquor on his breath.
Rick had just received his black belt in Karate and was not about to watch this man bully a child and his mom. To be honest, he was a little excited at the chance to put his hard-earned skills to use. Rick rose from his seat, chest out like a Marine’s, and moved swiftly down the bus toward the offender. His every move signaled confrontation.
“Hey!” a voice rang out toward the disheveled man.
But the voice was not Rick’s. It was that of an old woman observing the situation from the rear of the bus. The old woman smiled warmly, leaning at an angle to see through the crowd.
“Young man, I have a seat for you right here!” she shouted up to the front of the bus, patting an empty seat next to her.
The man paused, his eyes darting back and forth, and then he made up his mind. He tromped his way unsteadily past Rick to the back of the bus and sat down next to the old woman. “Aren't you glad spring is finally here?” the elder asked with excitement in her voice. “I'm on the way home to garden with my husband. You should see how happy he is when he gardens, and the hydrangeas are just about to bloom!”
The man wrinkled his face, blinking, and his lips began to quiver. Within seconds, a tear had broken free and began to roll slowly down his cheek.
“I was a husband once, and my wife gardened with me every spring,” he managed to stammer. “I lost my job last year, and then lost everything.”
The old woman patted his shoulder, a small gesture to acknowledge his grief. And as the two talked and the story unfolded, the rest of the bus silently listened in, faces softening with compassion.
At the next stop, W. Web watched curiously as Rick stepped off the bus. Five minutes and two miles ago, the stranger sitting next to him had been ready for a fight, but the man getting off the bus was different, with all the look of someone contemplating the hard blows life can deliver.
Story adapted from Steve Hagen
Whoa. W. Web’s adventures are getting a bit heavy for a technical book, and this is only Chapter 3. But his bus ride brings up an important point: Our assumptions and expectations play a powerful role in how we think about and experience the world and the ways in which our plans for action unfold. Two men on the bus experienced the same event with two different ground assumptions about the world. The situations that manifested for the two were entirely different as a result.
Software design and development follow this same pattern. At the beginning of a development project, before any design is laid down on paper, we have already made several important choices even if we don’t realize we have made them. These choices that is, our assumptions about the way that software should be developed shape the direction of the design and development process.
When programmers make these choices deliberately, we call them abstractions. Many people think of programming abstractions as class definitions that encapsulate some piece of functionality, but classes and objects are just the actors in your play. Abstractions also determine the rules of the game, all the way from turning voltage into binary logic to allowing memory allocation and database access. Abstractions encompass every environmental detail we have constructed to help us write computer programs.
It isn’t surprising, then, that choosing the right programming abstractions is perhaps the most important and influential step in the software development process. It should always be done intentionally instead of left to our unconscious assumptions. Good developers don’t write more code than poor ones; they paint better abstractions so that each line of code they write is more meaningful.
This chapter examines the basic abstraction that defines Ruby on Rails more than any other: that a web site is actually a Model-View-Controller (MVC) application.
This choice seems innocuous at first, but as any script-turned-Rails developer will tell you, it has far-reaching effects on the way you design and develop your code. This chapter takes a quick, light-weight look at what the Model-View-Controller paradigm is and why it aligns with the Web so well. I also cover some project design and refactoring strategies to help you on your way toward MVC-grounded design.
The bus continued down the road and eventually onto a bumpy highway. Between the hum of the wheels and the grain of the road... more
The bus continued down the road and eventually onto a bumpy highway. Between the hum of the wheels and the grain of the road shaking his seat, Web began fighting to keep his eyes open, and before he knew it. . .
“Excuse me,” said a frail voice, its owner tapping him softly on the shoulder.
Web opened his eyes, momentarily confused about where he was. He looked with the distant gaze of interrupted sleep at the elderly man who must have taken the seat next to him.
“I’m sorry to wake you. Your head was getting heavy on my shoulder, and you were drooling on my coat.”
Web blinked and felt consciousness returning to him. He hadn’t even seen the man get on.
“I’m terribly sorry,” he said suddenly as the ability to speak clicked in. “It’s been a long week. I’ll try my best to use the window.”
And then, to try to redeem himself, he said, “I’m the World Wide Web, you know.”
“That’s nice,” the man smiled through his thick bifocals. “I thought you looked like a Jeffrey.”
Web gazed out at the road passing by and thought of his week at the retreat. Everything had been so regimented, yet it all seemed to work together smoothly. The more he learned how to fall into their pattern of doing things, the less he found himself distracted by the day-to-day worries that had once kept him busy.
It was still a ways before Web’s stop: the protest down in the Big City. The members at his new camp said they were pleased at how well he understood their beliefs, and they wanted him to represent them at the event.
URLs were planning to gather to protest for equal rights, it seemed, right under the marquee at Application Square. His job was to attend the protest and attempt to identify and arrange a meeting with the leaders of each respective group that was there. Web still wasn’t sure what to do after that, but his new friends had assured him that the rest would take care of itself.
Learning to think about web applications in the context of MVC is the most important step in good Rails design. This overarching design strategy shapes everything else that you code and, done correctly, will keep your web applications easy to code and maintain. This chapter discusses what comes next: the design issues that crop up when building your models, views, and controllers.
The greatest task you face from a design standpoint is arranging your code so that it makes sense, minimizes waste, and stays true to the MVC pattern. Sometimes this is easy (HTML always goes in the view), but other times it isn’t so apparent (when and how should you check for SQL injection attacks?). As always, part of the fun is also learning where you can lean on Rails’ automation to keep your code concise and easy to read. So follow along as I tackle M, V, and C in order and look a bit more at how they are coded.
The Best API Documentation Is Free
This is a book about design, not syntax, but it is tough to talk about design without a knowledge of the language and API underneath. Although much of the content of this chapter uses the Rails API (this is a Rails book, after all), it does so without much explanation of the code that demonstrates the concept. Luckily for you, the best Rails API documentation is free! So instead of simply reprinting the excellent API documentation that already exists, this chapter provides the following table pinpointing where to find the documentation for each type of task.
W. Web stepped off the bus and forced a nervous swallow as he entered the crowd of noise and signs. He had arrived as close... more
W. Web stepped off the bus and forced a nervous swallow as he entered the crowd of noise and signs. He had arrived as close to the heart of Application Square as the bus could get, which was about two blocks away. As far as he could see, URIs and other regulars of their crowd were holding signs and milling about.
It was not unlike a football game, Web thought, remembering videos of tailgating parties that had been uploaded to him before. He waded through the URIs trying to get closer to the square.
The URIs were tall, French fry–like creatures with short, stubby legs and tyrannosaurus-like arms. For some reason, they all talked as though they were from the Bronx, even the Unicode ones. As groups of them chanted their slogans, their nasal voices occasionally cracked at the exciting parts.
No wonder these guys get pushed around, Web thought.
The real geeks were the ones who hadn’t even bothered to register domain names for themselves. IP addresses blazoned on their chests, they were a bit pastier than the others and wore thick glasses.
“Hey, watch it! Geez, buddy, can’t we even get some respect at our own protest?”
Web had been searching for the stage so intensely that he had inadvertently walked straight into one of them.
“Oh! I’m sorry. . .uh, 208.97.177.118,” Web said, tilting his head sideways to read the IP address written on its chest. “I was just looking for the main stage. Do know if there is one?”
“What am I, DNS? That resource is 301 Moved Permanently! Aheiahahiaha!” the URI snorted in reply.
Web stood there, unsure of what to say.
“Hey, loosen up, buddy,” the URI punched him in the shoulder. “It’s a beautiful day. The stage is over that way,” he gestured with his fry-like head.
“Thanks, I appreciate it. Say, what exactly are you protesting?”
“You mean nobody gave you a pamphlet?” the creature exclaimed. “Those dynamic URIs. You can never count on them to be there when you need ’em. We’re protesting the ’stablishment, ’cause we get no respect and it's time we deserve some.”
“What do you mean?” Web said. “Everyone uses you guys. You’re great you should see how large my browser’s bookmark folder is! In fact, just the other day. . .”
“Use us and throw us away; use us and throw us away. That’s the way it always happens. Throw-away labels, that’s what we are. Nobody stops to think that we’ve got depth to us. Do you think we like being three-hundred characters long? ‘Oh, doesn’t matter,’ you say ‘nobody ever stopped to appreciate the beauty of just a mere URI!’ Just a mere URI?!”
The URI didn’t give Web a chance to respond.
“And social services we deserve them, too. Do you know how much funding unit tests got last year? Over 1.2 billion. Billion! Ya know how much we got? A big zero. Zilch. Nothin’. How is it that we’re the face of the Internet and nobody’s ever stopped to think that we need attention, too?”
Web could see that this was the beginning of a long rant this URI had given many times before, and he knew he had to get to the stage before the protest ended.
“I couldn’t agree more. We need more funding devoted to URIs,” he said hurriedly. “Look, I’m sorry to run, but I’ve really got to get to that stage. Thanks for your help!”
And before the URI could say anything more, Web had pushed back into the crowd, weaving his way forward toward the edge of the square.
The exciting thing about a good design is that the more you dive into it, the more you appreciate it. In this chapter, you’ll see how a good MVC-grounded application design begins to pay serious dividends as you expand your web application past Version 1. This chapter explores the design, implementation, and metering of Web APIs with Rails.
Offering an API to your web application is becoming increasingly important for success. It gives users better control over the data that your site provides and opens the door for creative reuse of your data. Although you might not benefit directly from ad clicks on pages reusing your services and data, you will benefit from the publicity that popular API users will generate for you and can strike for-fee usage agreements with heavy users.
Web APIs also promote a vision of the Web in which sites are able to specialize their focus and work together to more efficiently achieve programming goals. Few web developers today would implement their own in-page map widget. Instead, they would just use one of the excellent Map APIs offered by Google, Yahoo!, and Microsoft. Reusable parts and the ability to specialize are key factors that accompany the advancement of any area of technology. Using and developing APIs on the Web enables increased quality and sophistication of web applications for all users.
Web Service can be a confusing term because the phrase represents both a general category of software development and a specific set of standards. It is as though you created a company to produce and market your amazing new granola bar but named this bar “Food.” Now you’ve complicated everyone’s ability to communicate clearly; every article about food must now begin with a clarification of which food it is talking about (food or Food) and a reminder of what the differences are. To settle this problem here, I use the term Web API.
This chapter is about Web APIs and Ruby on Rails. It explores the Rails-style API and demonstrates how to successfully integrate one into your application. Except for a small segment about ActionWebService at the end of the chapter, this chapter is not talking about SOAP or XML-RPC but rather a new style of API design popularized by the Rails framework. This style encourages you to consider web site design and service API design as two faces of the same effort rather than as two separate and distinct components of your web application.
“COM! NET! ORG! We represent much more! COM! NET! ORG! We represent much more!” The chanting grew louder as Web neared the... more
“COM! NET! ORG! We represent much more! COM! NET! ORG! We represent much more!” The chanting grew louder as Web neared the square. A stage was set up right in the center, and an empty podium waited for the gathering’s speakers to begin their addresses. Web could feel the buzz that permeates the senses whenever people assemble in a herd the echoing chants, the waving signs, motion, aspiration, all in unison.
Up near the square, the composition of the crowd was more diverse not just URLs but protocols, interpreters, libraries everyone gathered to support the URLs. A few technicians appeared on the stage and began tinkering with the microphone.
“Testing, testing,” a skinny URL tapped on the microphone, his voice cracking. “Looks like we’ll be multicasting today, ahiehaha!” he said to cheering crowds.
These guys just don’t quit, Web thought to himself, shaking his head. But why am I here? Who am I supposed to meet?
And then Web caught her eye, up near the stage but off to the side where the crowd was sparse a woman with the unmistakable look of someone who was above the fray. She held a walkie-talkie in her hand and stood with three others in deep conversation.
The woman said something and the other three turned and looked at Web.
Are you. . .? Web mouthed from across the square, not knowing what else to say but remembering what he had been told back at the compound.
Just show up and stay alert. Everything else will take care of itself.
Yes. The woman mouthed back, nodding her head. One of the men to her left waved his arm and beckoned for Web to come over. Web began making his way across the street.
“You must be Web! We’ve been waiting for you!” the woman greeted him as he neared. “I’m Jen. Rusty told us to keep an eye out for you.” She shook Web’s hand. “And these are my colleagues, helping me keep things in order for the events today.”
“It’s a pleasure,” Web said, “but I have to be honest; I’m not exactly sure what part I play.”
“Don’t you worry, everything has its place that will soon be apparent if nothing else. I’m terribly sorry but right now I have to run. You wouldn’t believe the mess that’s just happened with the water truck. Someone gave the driver a private IP address to deliver to and he never made it out of the warehouse parking lot. Anyway, I’m supposed to give you this.”
She handed Web a business card. On one side was his own name embossed in black ink, and on the other side was a map leading several blocks away from the main square to some place named The Underground.
“It’s a restaurant,” Jen continued. “We go there whenever an event is in town. Be there an hour after the crowd dissipates, and the rest of us will come meet you. It will be a much better place for talking.
“Jen! The URLs say they broke the mic! Where did you put the backups?” A man with a clipboard shouted from the corner of the stage.
“Hold on!” Jen rolled her eyes and gave Web a reluctant look, hoping to end the conversation.
“It’s okay,” Web said, “Go ahead. I’m looking forward to listening to the speakers, anyway.”
“Thank you. I’ll see you later on!” She turned, alternating shouted instructions between her walkie-talkie and the stage hand, and then disappeared behind the platform.
Web stayed where he was he preferred the sidelines where there was room to move and watched the crowd mill about before the speaking began. He had found the people he was supposed to meet, but the question still lingered in his mind. Why am I here?
Representational State Transfer, or REST, is a design style that leads to truly “web native” applications that take advantage of the characteristics of the HTTP protocol and resource-oriented programming. RESTful development affects all aspects of your web application right from the start. At the design stage, REST provides guidelines that help you determine how to model your controllers and user-facing objects. During the implementation stage, REST allows you to take advantage of standardized resource operations provided by HTTP. And after your application is deployed, it makes you part of the REST-based Web Services community with little-to-no extra effort on your part.
To many web developers, REST is an architectural style for the web in the same way and with the same importance that MVC is an architectural style for application code. Both offer a systematic way to organize code, both provide a standard and predictable control flow between components, and both do so using the tools that you already have in front of you. The first half of this chapter explores the design style and implications of REST, and the second half shows how to use Rails to publish and consume RESTful resources.
Web sat waiting in a booth at the back of the smoky pub, staring at the aged carving in the wood above his table.... more
Web sat waiting in a booth at the back of the smoky pub, staring at the aged carving in the wood above his table.
LOKE ERST YE THENK, AND BE YE THE WOLRDE
And the phrase beneath it, in the same ornate style but newer, he thought, given the brighter color of the wood bezel.
FRIDAY NIGHT FISH AND CHIPS SPECIAL
The pub was a strange maze of tables and shadows, wood and brick columns holding up the floors above. It was buried underneath an otherwise normal building holding commercial space, an old wine cellar that used to belong to a distributer on the seventh floor. Before that it was the factory floor for a parachute company, and even before that it was rumored to have been an underground alchemy supply shop.
Now it was a pub. A damp, earthen smell permeated through the sounds and odors of fried fish and pipe smoke.
“Hi! My name is AJ, and I’ll be your server today! Is there anything I can get you to drink?”
Web thought the waitress looked out of place in this old tavern. She beamed at him, waiting for his reply with her spiked, neon-blue hair; she wore a shining pink plastic halter top and costume-like makeup. She was balancing a tray of empty pint glasses on one hand and a skateboard beneath her feet.
“Actually, liquids tend to give me a short. Still some old ISDN lines in here,” Web said, patting his stomach. “Could I just have some chips? You know what, throw in the fish, too.”
“Sure thing. No liquids. Fish, chips, coming right up,” she said as she rolled away.
From afar, Web could see the dim outline of the maître d’ directing a group of people toward his table. He recognized Jen from before, along with three others he didn’t know. He stood up to shake their hands as they approached the table.
“Web, it’s good to see you again! I’m sorry I had to run so quickly before. Let me introduce you to my friends ”
“Napkin!”
AJ cut through the group of people on her skateboard and left Web with a napkin in his outstretched hand.
“Rusty, Matz, and Schema,” Jen continued unfazed.
Web gave each a hearty greeting, and the five sat down in the booth. Before the newcomers even had time to pick up their menus, AJ was back, tossing a plate down on the table.
“Plate!” AJ’s voice trailed past as she skated without slowing, the empty plate left rattling in a circular motion on the table.
Matz looked at the spinning plate and inspected the cover of the menu closely. “How strange. I’ve heard of these new places. What did you order?”
“I got the fi ”
“Fish!” AJ was flying by on her skateboard in the opposite direction this time. She managed a perfect shot from four feet away; the fish landed squarely on the far side of the plate and didn’t even slide a centimeter.
“Fish. And chips,” Web replied, starting to worry about how his knife and fork would be delivered to him.
What we now call AJAX was once a great idea hiding in plain sight. For years, the XMLHttpRequest object sat with little notice or use, first in Internet Explorer and later in Mozilla. Then in the spring of 2004, Google began beta-testing a new type of web-based e-mail client called Gmail, and overnight, the incredible potential of AJAX just made sense (with the feeling of “duh, why didn’t we think of that?” that often accompanies a discovery in plain sight).
XMLHttpRequest
From its inception, the Web grew up around a “one document, one request” paradigm that made perfect sense for the document-centric environment it was created to serve. To display multiple documents at a time, the web community created the notion of “frames,” walled-off divisions within the page that each loaded its own separate document, not unlike the Picture-in-Picture feature on large TVs. Developers could control these frames to a reasonable degree with JavaScript. But what if a web browser didn’t need to deal with entire documents at a time, and instead could make requests that replaced just tiny chunks within the existing document?
AJAX (Asynchronous JavaScript and XML) is the technique born of that “what if.” Technically, the term refers to the use of asynchronous JavaScript requests for XML data. In practice, AJAX refers to the entire collection of common tricks and patterns that have grown up around the XMLHttpRequest and the way it can transform web development both asynchronously and synchronously, with both XML and with HTML.
Developing with AJAX can be tricky, though, because it has a tendency to lead astray that elegant design you’ve been working on for the past few months. The reason is that AJAX is every bit as much a paradigm shift for the Web as the <img> and <form> tags were a decade before. Nearly all the collective wisdom on web application development centers on the idea of the web site as an application. Suddenly, with AJAX, a single page can now be deserving of this title.
<img>
<form>
This shift in potential from site as application to page as application is the difference between CNN.com and Microsoft Word. It is the difference between forever writing styled-up database front-ends and writing full-featured, interactive applications. It opens the possibility, if you want it, to completely throw away the document paradigm of the Web and use HTML just as domain-specific user interface (UI) language. In other words, AJAX is a big deal.
This chapter introduces you to some of the design issues in AJAX and shows you how to integrate AJAX smoothly into your Rails applications. I discuss the different styles of AJAX development and describe how to design your sites so that you can strike a balance between the middle of two extremes: not quite a single-page application, but not the page-heavy web applications of yore, either.
W. Web listened intently as his companions bantered back and forth as if according to a script. They had been sitting at... more
W. Web listened intently as his companions bantered back and forth as if according to a script. They had been sitting at the table for some 40 minutes, most of them spent discussing the day’s protest and how it had gone. This group, it turned out, was exactly who Web was sent to find prominent representatives from the clans allied with his, sent to show support and presence at the URL protest downtown.
Wall-mounted oil lamps flickered oranges and yellows off the shadows in the basement tavern as discussion of the protest moved to one of motivation, as such conversations tend to do. The occasional shouts and nasal laughter of a group of lingering URLs, their scrawny, fry-like bodies in baggy homemade t-shirts, could be heard amid the clinking of glasses from the bar across the hall.
“Flexibility,” Schema said, slapping his hand definitively down on the table and looking at Web. “What we need is some flexibility. You have no idea what it’s like having to do the same thing all day.”
“But what else is there to do?” Rusty responded. “You create data; you update it. If you’re lucky, someone might come and read it, too. And then one day, you send it to the big bit bucket in the sky.”
“That’s exactly the kind of myopic response that gets us into this type of problem. Look at us! You, six feet tall. Me, with a mustache! People grow, things change. We don’t always know what we want to do with a thing at the time we build it.”
“But there’s a balance to be kept. The best we hope for is some grounding stability amidst the process of change. Certain structures to scope and limit our operations.”
“Look,” Matz jumped in, “I think we can all agree for the needs of both flexibility and structure, but I’m not so sure we need to characterize the solution as a tension between the two. Could we not attempt to live each through the eyes of the other?”
“Agreed,” Jen offered. “What is most important is that we take a holistic approach that incorporates all of our strengths. We can’t afford to factionalize ourselves in the face of. . .” she trailed off, looking uncomfortable.
Faces grew somber and nods accompanied downward stares around the table. A URL laughed loudly in the background.
“In the face of what?” Web replied.
His party looked nervously among themselves, communicating with their eyes in a silent, reluctant argument to decide who would be the one to explain. Rusty leaned in toward the table, the rest of the group following.
“The compilers,” he said in a near whisper, almost mouthing the word. “They have spies, so we can’t discuss it in the open. This rally was a cover for the resistance movement. The secret negotiations have fallen through, and the time has run out.”
Web wanted to disregard the statement as silly, but the solemn expressions on the faces looking at him made shivers run down his spine and out his limbs. The air suddenly felt electrified in a way that could not be denied.
“What do you mean, run out? What resistance movement?” Web said, his voice now low and intense.
“There’s a war coming, Web! And it’s too late to stop it! They’re gathering by the day. We’ve been tracking them for a year now. But the final preparations have only recently begun.”
“What was the protest for, then?”
“To safely gather the leaders of the resistance movement under the guise of an internal struggle,” Jen replied. “Until last week, we had met only through messages, passed through unreliable transport.”
“And me? Why was I sent here?”
“Because this war is about YOU, Web” Rusty said, his voice a strained shout hidden in the whisper. “You need to start paying attention to the events around you! Stumbling around on the street like you do!”
“You were sent here so that you could be protected,” Jen said in a much softer tone.
Web sat back in the bench, stunned.
“Their signal to attack is the Flying Toasters screensaver.”
It was too much to process, but he could see the connections starting to form. The Flying Toasters screensaver? The screensaver! He had been processing orders for that screensaver for months! The biggest resurgence of a software product in years! And the compilers. Nothing on the Web was compiled; his very existence was a threat to compilers everywhere!
Web absorbed the air's electricity as he looked at the faces of his new colleagues, and realized, for the first time, why he was here.
It had to happen eventually: a book on the Rails-style of programming delving specifically into Ruby. As you have seen over the first half of the book, Rails is just as much a style of web development as it is a framework that implements that style. But the way in which Rails style is revealed in code cannot be separated from the Ruby language on which it is constructed. These next two chapters will explore some of the finer parts of the Ruby language that make it so different from the traditional OO establishment. These “Rubyisms” make the Rails school of design and development possible.
The software development taught en masse these days tends to emphasize the C/Java programming style in which writing code is about creating highly specified objects that manipulate data and other objects. If you started programming in this type of environment, just think about every method signature you ever wrote. If you are like most of us, your methods took parameters with types such as int, String, and List. This type of programming creates a clear separation between “that which is code” and “that which is data.” The code part is static, defined during code-time, and the data part is limited to declarative containers of numbers and characters.
int
String
List
The truth is, there is a rich history of programming languages that don’t limit the code to just talking about data manipulation but actually allow you to write code that talks about other code as well, and even code that writes other code while the program is running. Ruby is one of those languages. This type of programming is usually referred to as metaprogramming, defined by Wikipedia as:
The writing of computer programs that write or manipulate other programs (or themselves) as their data…In many cases, this allows programmers to get more done in the same amount of time as they would take to write all the code manually.
This chapter begins a three-chapter series on advanced Ruby and metaprogramming techniques. Learning these skills is essential to becoming an effective Ruby programmer, and after you’ve learned them, it will be hard to go back to languages without them. These three chapters will change not just the way you write code but also the fundamental ways in which you think about your design and how it might be implemented, providing new and more flexible ways to describe yourself to the computer.
This chapter is about blocks and Procs, together forming a feature of Ruby that allows you to ball up code into a package, bind it to an environment, pass it around like a variable, and execute it at will. If you have a background in functional languages such as Scheme and Lisp, blocks will seem right at home to you. If you come from an object-oriented background, blocks will at first seem exotic but will slowly become one of the most pleasant surprises of the Ruby language.
A firm grasp of blocks will help you write clean, reusable code. In the last section of the chapter, I demonstrate several design patterns that use blocks, using examples from the Ruby on Rails framework. With this knowledge, you will have a better understanding of how Ruby on Rails works and how you can extend its style of programming for your own purposes.
The five left the pub after Jen and her friends had spent a long time explaining the details of the terrible conflict that... more
The five left the pub after Jen and her friends had spent a long time explaining the details of the terrible conflict that was unfolding. Web had listened calmly he wasn’t sure what to be other than calm but he was in a state of disbelief at what he was hearing. The whispers, the conspiracies, the web sites visited by crazy radicals! It was true!
The sun had long gone down, and the streets outside were empty echoes of the hordes that had filled them hours before. Web’s companions were businesslike, determined. The five were to go to the resistance headquarters that had been set up in the Big City. Now that Web had been found, he would be kept safe.
“I understand the facts of what you are telling me, I just don’t understand why. Why? How does anyone benefit from an attack? The compilers must know this,” Web said almost to himself, breaking the silence as the five walked down the deserted city street.
“Change, Web,” Jen said. “Change and manipulation.”
Rusty continued for her. “Compilers just weren’t written to be dynamic. They accept the world as their designers envisioned it. Nothing else makes sense to them. Nothing else can make sense to them. They don’t hate the Web because it is competing with them; they are scared of it because they can’t understand it.”
“And that doesn’t make them bad,” Jen cut in. “They do what they were designed to do much better than we ever could but their role is a simpler one. They are meant to follow the orders they have been given, not to understand and adapt to new situations. From our standpoint, that is the painful tragedy of it all.”
“You mean ” Web gasped.
“Yes,” Jen continued. The rest of the group nodded. “Somebody is at the top sending the compilers directives that they have no choice but to follow. Most of them are innocent pawns, with no idea what they’re doing. They understand enough vocabulary to follow orders but have no facility to add the code necessary to understand what those orders mean, to reason about them.”
The five turned left at an intersection and continued down another street. From an electronics store ahead, the dim, bluish glow of television sets cast an eerie light on the sidewalk in front of them.
“Worst are the old compilers, bless their souls. Thirty forty? years of service and this is how they are repaid. Being utterly used, tricked into working for an ill that they don’t understand.”
“So who is at the top? Who is giving the orders?” Web asked.
“Hey guys, look at this!” Schema had fallen behind at the window of the electronics shop.
The group turned and looked back. Schema was staring at the array of television sets. The blue glow flickered on his face as though the transmission were being interrupted.
“Something is wrong with the signal. It keeps going to static and then displaying fragments of code. You don’t think ”
A gasp of air. Schema froze, breathless, eyes fixed on the set in front of him. His back straightened and arched; his neck tensed. Silently, slowly, he fell to the sidewalk.
And then he began to convulse.
Jen’s scream pierced the silence of the empty street.
Chapter 8 discussed some of the ways that Ruby allows code to be bundled together and executed, and it introduced you to the paradigm of block-based programming. Blocks allow developers to bind a region of code to the local scope but send it somewhere else in the program for execution, making it, in many ways, a more flexible version of a callback.
This chapter will show you how to bundle code together and execute it in such a way that it affects objects other than those from its source environment, allowing you to use blocks to change the structure and workings of any object in Ruby while it is running. In addition, you will be able to trigger the execution yourself, making any class and object fair game to have your custom code invade and start running. This capability has two broad categories of use, which this chapter also covers:
Mixins: Mixins are Ruby’s answer to multiple inheritance; they allow you to collect functionality into modules and include them as needed in a class. This chapter will show you how to mix functionality into your objects as needed while the program is running.
Monkey patching (or duck punching): Monkey patching is the practice of modifying someone else’s code while it is running to change its behavior to better suit your needs. You might think of it as a dynamic way to mix in functionality into a class or a class instance that lets you write the code on the fly and modify any object of your choosing.
These two activities are similar in effect but different in intent. Mixins provide a way to add functionality to objects in parcels, allowing objects to retool their functionality in the middle of program execution. They are a critical tool that Ruby provides to help you manage large code bases because they form an easy way to separate and group code into small modules and recombine these modules as needed into larger objects.
Monkey patching is most similar to dynamic subclassing without the subclass: invading the code of an object at run-time to surgically replace certain portions with your own version. The code of the target object never changes on disk, but throughout the execution of your program, your modifications take precedence over this “official” copy. Monkey patching can be a controversial topic to Ruby developers because the style of coding it encourages can cause unexpected problems and can make a code base difficult to manage. Nevertheless, monkey patching is also an important part of Ruby development because it provides a way to experiment and preview changes that you would like to make more permanent later on.
When you have finished this chapter, you will be able to examine the Rails source with a better understanding of how classes and modules are organized, and you will know how to customize the behavior of the Rails framework by dynamically injecting your own code into its objects.
The four rushed to Schema’s side, but it was too late.
“Don’t look at the television sets!” Rusty shouted in a choked voice... more
“Don’t look at the television sets!” Rusty shouted in a choked voice.
Schema’s body was lifeless, his eyes fixed forward in a shocked stare.
“Pick him up! We have to get to the headquarters! Matz!”
As the others moved to pick up Schema’s body, attempting to displace their anguish with immediacy, Matz stood there staring at Schema, his mouth moving but nothing coming out.
“i…instance. instance_eval,” he stammered, watching the others hoist Schema onto their shoulders. “They used instance_eval. Our own strength. How could they what sick mind would I wrote that feature.”
i
instance
instance_eval
Matz’s vision was a blur. How many Ruby programs were watching television when the code broke through? It was mass murder. It was his fault! Schema! He clutched his head.
Rusty placed his hand on Matz’s shoulder. “Matz. We have to go and we have to go quickly. It’s not your fault. Pull it together.”
“You you don’t understand. They must have sent code through the airwaves.” Matz was in shock. “Any agile program who saw it would have executed it.”
The other three looked at each other, the words sinking in. The wind blew a chill down the street.
“Rusty’s right,” Jen finally said. “We have to go.” The four of them hoisted Schema onto their shoulders and began walking down the street in brisk silence. Web felt as if he had entered a dream. We want you to go to a protest, they had said. It will be a great first trip for the group. Now he was walking on a street he didn’t know, with strangers he had just met, carrying the body of someone whose only crime was looking through a storefront window.
At the next block, the sounds of tires screeched around the corner, followed by the flatbed truck that owned them. It was a strange truck, not the type you see often in cities. The truck bed was made of plywood, with no walls, and it was painted a dull black. Two URIs were in the cab. It skidded to a stop.
“Hey! Get on! Quick!” they shouted to the group. “We’ve been looking everywhere for you!”
Web looked to Jen, and Jen nodded. These were friends.
This chapter is the third and final chapter focused on advanced Rubyisms and meta-programming. You saw in Chapter 8, “Playing with Blocks,” that Ruby lets you pass blocks of code around as if they were variables, and you learned how to style your code to make use of this feature. Chapter 9, “Mixins and Monkey Patches,” showed you how to inject functionality into objects, whether as a design technique with mixins or as a monkey patch with instance_eval and class_eval. In essence, the last chapter showed you how to dynamically modify any object’s code at run-time. This chapter goes a step further and shows you how to write code that will dynamically modify itself.
class_eval
Writing code that changes itself is very different from monkey patching. When you monkey patch, the desired change is known in advance decided during development by the developer. Self-modifying code or, perhaps better put, adaptive code is a different ballgame. This type of code has to be written one level of abstraction away from the change that actually takes place. Instead of writing code that contains the changes you would like to be made, you have to write code that describes how your objects can decide for themselves what changes need to be made. Don’t worry, though; we’re not talking about artificial intelligence here, but rather just another powerful style of programming that the Ruby language allows.
This chapter shows you how to write objects that have the ability to add new features to themselves as needed at run-time. Sometimes these new features are added explicitly and proactively by adding new methods to an object based on some macro that has been called. Other times these new features are added implicitly, through the use of handlers that respond to method calls for which no method existed and determine how to best fulfill the request. Both of these capabilities of Ruby are at the heart of the streamlined experience that Rails presents to the developer.
Although the potential uses of this chapter's concepts are great, their primary benefit lies in three simple lines of code:
def User < ActiveRecord::Base
has_many :photos
end
These three lines of code accomplish as much for a Rails developer as do pages of Java. The extension of the ActiveRecord::Base class links the object to the database table of the same name and automatically infers search, getter, and setter code for its fields on the fly. The has_many macro affects the object in a different way, causing a series of associative methods to be added to the object dynamically as it is parsed that is, it writes code, which is both a scary and wonderful concept. This chapter shows you how to accomplish both these feats and touches on even more patterns that this type of coding pairs well with.
ActiveRecord::Base
has_many
When reading this chapter, keep in mind that computer code always has two audiences who experience the code in very different ways: the end-user, who experiences only the output of the code, and the developer, who must work intimately with the code after it has been written. An often-referenced Rails philosophy is that better code leads to developer happiness, which leads back around to better code. The techniques shown in this chapter are developer focused; for every example you read here, there is almost assuredly another way to accomplish the same end result with more traditional coding practices. This chapter will show you, however, how to accomplish those end results with a style and concision that will pay off in developer happiness points.
The elevator doors slid open to reveal a futuristic command center worthy of monitoring the Internet itself. It should be... more
The elevator doors slid open to reveal a futuristic command center worthy of monitoring the Internet itself. It should be so worthy that is exactly why it was built. Deep in the heart of the Big City, unbeknownst to all but a few, was the INOC, the Internet Network Operations Center.
“What do you mean we can’t track them!” A deep, gruff voice boomed from the heavyset man standing behind the analysts.
“They don’t have network connectivity, sir. There’s no way to get a trace,” a URI at one of the terminals said nervously.
“Well, I need a trace!” the head man shouted. “Bryant! What do you have over there? Where’s our exploratory team?”
A man on the side of the room almost dropped his coffee upon being called out. “Ummm,” he stalled, shuffling through scattered papers. The stalling saved him, because the man in charge soon noticed Web and the others standing in front of the elevator doors.
“Web! It’s you!” he strode across the room to the group, not noticing Schema’s fallen body until he got there. His voice lost its military bark and his expression softened.
“Schema, too,” he said quietly, shaking his head. “None of us saw it coming.” He looked up at Jen. “I’m glad the rest of you are safe. Come with me into the briefing room and I’ll catch you up on what’s happening.”
The glass-walled briefing room sat off to the side of the INOC and had just enough room to fit the large conference table that had been installed. Jen, Rusty, Matz, and Web sat on one side of the table with the man, who Web now knew was General Operand, on the other side.
The General spent half an hour explaining the events that had unfolded. Web listened intently. Jen and Rusty battered him with questions. And Matz stared silently into the empty space in front of him. He hadn’t spoken since the street.
Matz was right they had broadcast an exploit across the television waves that destroyed the code of any Ruby program who happened to encounter it. It had caught hundreds off guard that they knew about; maybe there were thousands more.
“But what about the toasters?” Jen protested. “It doesn’t make sense.”
“We misread the toaster intel, Jen. Flying Toasters was the name of the virus they broadcast. The screensaver purchases were just a ploy.”
The General explained to them that the URIs on reserve were being called together to mount an offensive. Root DNS Server buildings were being used as the rendezvous points. The on-duty teams that were ready and waiting had already been sent out.
“They hit us hard with a surprise attack up front, and it’s going to get harder,” the General said, holding nothing back. “Frankly, I’m not sure how to proceed. We know that at some point, they have to attack us in the real world, but we can’t track their movements because the compilers come from another age. They’re all off the grid.”
“If we don’t figure something out soon, I’m afraid it will be too late.” He looked at Web with an expressionless face and said quietly, “I’m sorry, Web.”
Matz looked up from his blank gaze and straight at the General.
“General,” he cut in, “I think I know what to do.”
The database is the unsung hero of web development. The web page provides the aesthetics and excitement, and the application logic provides the features; it's easy to see how the off-the-shelf data repositories get forgotten in the mix. But at the same time, they are the keystone of web application development because they provide the single and reliable source into which all state for the application is kept. This chapter discusses how to streamline database development and use the Rails framework to enhance the simple object model that is built into databases. It starts with the concept of migration-based development and finishes with features such as serialization and Single Table Inheritance.
“That’s crazy enough that it just might work,” one of the technicians said, leaning back in his chair.... more
“That’s crazy enough that it just might work,” one of the technicians said, leaning back in his chair.
They had gathered everyone in the main room of the INOC to go over the plan. Back in front of the larger audience, General Operand had returned to his public persona.
“Of course it will work!” he barked. “Matz says it’s flawless.” He slapped Matz hard on the back, sending him stumbling a few steps forward.
“Not quite flawless,” Matz said as he looked up at the General.
“What do you mean ‘not flawless’? You said you were sure!”
“Well, for one thing,” Matz explained, “we can’t be sure what object types they are programmed to expect unless we can get a copy of their spec documents, which isn’t possible in so little time. So we’ll have to pick something as unexpected as possible. Something totally random.”
In a fitting retaliation, Matz had devised a plan to use the compiler’s very inability to dynamically modify code against them. It wouldn’t kill the compilers, but it would stun them until revived, which also fit the resistance objectives; compilers were just pawns in this game. As far as General Operand was concerned, the compilers were to be “fought with the care you’d fight a brother!”
The plan hinged on the hard-typing of their APIs. They had strict orders, a strict API, and strict callbacks. If the other side was going to fight dirty and take advantage of language features, so would the resistance. They could throw any object they wanted straight into the unsuspecting API callbacks, and the compilers would crash if it were an object type other than expected.
“Excellent!” the General roared. “We’ll just pick something they’d never expect. Bryant! What was that contraption you had in here yesterday?”
“Umm,” he was startled again by the General’s question. “You mean my humidifier, sir?”
“Exactly the thing-a-ma-what’s-it! We’ll use one of those!”
“There’s one more thing, General,” Matz added reluctantly. “We’re going to have to be close.”
“How close?”
“Very close. It’s going to have to be a callback. . .” He hesitated. “Which means we’re going to have to send some agents in there to get captured first.”
Test-driven development (TDD) is an important skill to learn as a developer and one that is especially prized by the Rails community. To the uninitiated, developing through tests seems like extra work at the least and an oppressive corporate-style burden at the most. But as those who follow this practice will tell you, the rewards of TDD far outweigh the work as the size and duration of a project progresses.
TDD is a style of development driven by the premise that you shouldn’t write tests just to check the code you write; you should write tests to define the expectations and drive the development of new code. “Test First, Code Second,” is a good distillation of the basic message. By writing your tests up front, you are forced into a mentality in which you design code with clean, small divisions of functionality, eschewing long, cryptic methods. If all goes well, the resulting code is not only easy to read and talk about but also defensible because the tests that validate it were developed even before the code itself was.
“If only large-scale projects actually worked that way” is the refrain you might hear from experienced developers when reading the last two paragraphs. Although TDD can be amazingly effective, in truth it is a difficult development style to truly achieve. It is hard to decide up front what to write your tests for, and even after you have decided, you don’t always know how fine a grain of capability to test. That is where this chapter hopes to step in with a new style of development that reorients the test-driven mindset toward stories, requirements, and behaviors.
This chapter is about behavior-driven development (BDD), an evolution of the TDD style that overlays an easy-to-understand vocabulary and set of development practices on top of existing expert advice about test-driven practices. These contribute to an overarching developmental story that helps developers define the desired characteristics of an application and identify where and how to test those characteristics.
BDD is rapidly gaining ground in the Rails community as a way to define requirements and expectations, guide development, provide reference examples, and test code all in one package. This chapter will introduce you to BDD and show you how to develop with RSpec, a BDD testing library for Ruby applications.
Purchase Before purchasing this product, please be sure you have met all software and system requirements, and that you understand any limits placed upon its use.
Return Policy Wrox Chapters on Demand are non-returnable and non-refundable.
Watermarking Wrox Chapters on Demand are sold with a small unique watermark at the bottom of each page identifying the purchaser name, email address, and order number.
Reader Software Wrox Chapters on Demand are offered as PDFs, and they can be viewed using the Adobe Reader, ADE, or a compatible PDF reader. If you do not have the Reader installed, it can be downloaded for free at Adobe.com.
Test Download As Wrox Chapters on Demand purchases are non-returnable, it is advisable that you test your system and software configurations with a free sample download before you place an order.
Usage Rights for a Wrox Chapters on Demand File Any Wrox Chapters on Demand product you purchase from this site will come with certain restrictions that allow Wiley to protect the copyrights of its products. After you purchase and download this title, you:
If you have any questions about these restrictions or need any further assistance please refer to Technical Support (www.wiley.com/techsupport) or call (877) 762-2974 (8 a.m. - 5 p.m. EST, Monday - Friday).
Related Books