Showing posts with label ruby. Show all posts
Showing posts with label ruby. Show all posts

Wednesday, March 4, 2015

Book Review: Ruby Wizardry



Continuing with the “Humble Brainiac Book Bundle” that was offered by NoStarch Press and Humble Bundle, I am sharing books that my daughter and I are exploring as we take on a project of learning how to write code and test software. Part of that process has been to spend time in the "code and see" environment that is Codecademy. If you have done any part of the Ruby track on Codecademy, you are familiar with Eric Weinstein’s work, as he’s the one who wrote and does updates to the Ruby section (as well as the Python, JavaScript, HTML/CSS and PHP modules).

With "Ruby Wizardry", Eric takes his coding skills and puts them into the sphere of helping kids get excited about coding, and in particular, excited about coding in Ruby. Of all the languages my daughter and I are covering, Ruby is the one I’ve had the most familiarity with, as well as the most extended interaction, so I was excited to get into this book and see how it would measure up, both as a primer for kids and for adults. So how does Ruby Wizardry do on that front?

As you might guess from the title and the cover, this book is aimed squarely at kids, and it covers the topics by telling a story of two children in a magical land dealing with a King and a Court that is not entirely what it seems, and next to nothing seems to work. To remedy this, the children of the story have to learn some magic to help make the kingdom work properly. What magic, you ask? Why, the Ruby kind, of course :)!

Chapter 1 basically sets the stage and reminds us why we bought this book in the first place. It introduces us to the language by showing us how to get it ("all adults on deck!”), how to install it, make sure it’s running and write our first "Hello World” program. It also introduces us to irb, our command interpreter, and sets the stage for further creative bouts of genius.

Chapter 2 looks at "The King's Strings" and handles, well, strings. Strings have methods, which allow us to append words like length and reverse written with dots ("string".length or 'string'.reverse). Variables are also covered, and show that the syntax that works on raw strings also works on assigned variables as well.

Chapter 3 is all about Pipe Dreams, or put more typically, flow control, with a healthy dose of booleans, string interpolation, and the use of structures like if, elseif, else and unless to make our Ruby scripts act one way or another based on values in variables and how those values relate to other things (such as being equal, not equal, done along with other things or in spite of other things, etc.).

Chapter 4 introduces us to a monorail, which is a train that travels in an endless loop. Likewise, the chapter is about looping constructs and how we can construct loops (using while as long as something is true and until up to the point something becomes false, along with for to iterate over things like arrays) to do important processes and repeat them as many times as we want, or need. Also, it warns us to not write our code in such a way to make an infinite loop, one that will just keep running, like our monorail if it never stops.

Chapter 5 introduces us to the hash, which is a list with two attributes, a key and a value,  as well as the ability to add items to arrays with shift, unshift, push or pop. Additionally we also learned how to play with ranges and other methods that give us insights as to how elements in both arrays and hashes can be accessed and displayed.

Chapter 6 goes into talking about symbols (which is just another word for name), and the notation needed to access symbols. Generally, the content of something is a string; the name of something is a symbol. Utilizing methods allows us to change values, convert symbols to strings, and other options that allow us to manipulate data and variables.

Chapter 7 goes into how to define and create our own methods, including the use of splat (*) parameters, that allows us to use any number of arguments. We can also define methods that take blocks by using "yield".

Chapter 8 extends us into objects, and how we can create them and use them. We also learn about object IDs to tell them apart, as well as classes, which allow us to make objects with similar attributes. We also see how we can have variables with different levels of focus (Local, Global, Instance and Class) and how we can tell each apart (variable, $variable, @variable and @@variable, respectively).

Chapter 9 focuses on inheritance, which is how Ruby classes can share information with each other. this inheritance option allows us to create subclasses and child classes, and inherit attributes from parent/superclasses.

Chapter 10 shows us a horse of a different color, or in this case, modules, which are a bit like classes, but they can't be created using the new method. Modules are somewhat like storage containers so that we can organize code if methods, objects or classes won't do what we want to do.  By using include or extend, we can add the module to existing instances or classes.

Chapter 11 shows us that sometimes the second time's the charm, or more specifically, we start getting the hang of Refactoring. Using or operators to assign variables, using ternary operators for short actions, using case statements instead of multiple if/elseif/else statements, returning boolean variables, and most important, the removal of duplicate code and making methods small and manageable.

Chapter 12 shows us the nitty-gritty of dealing with files. Opening reading from, writing to, closing and deleting files are all critical if we want to actually save the work we do and the changes we make.

Chapter 13 encourages us to follow the WEBrick Road, or how we can use Ruby to read and write data from the Internet to files or back to Internet servers using the open-uri gem, which is a set of files that we can use to make writing programs easier (and there are lots of Ruby gems out there :) ).

Chapter 14 gives some hints as to where you, dear reader, might want to go next. This section includes a list of books, online tutorials, and podcasts, including one of my favorite, the Ruby Rogues Podcast. It also includes interactive resources such as Codecademy, Code School, and Ruby Koans, and a quick list of additional topics.

The book ends with two appendices that talk about how to install Ruby on a Mac or a Linux workstation and some of the issues you might encounter in the process.

Bottom Line:

For a "kids book", there's a lot of meat in here, and to get through all of the examples, you'll need to take some time and see how everything fits together. The story is engaging and provides the context needed for the examples to make sense, and each section provides a review so that you can really see if you get the ideas. While aimed for kids, adults would be well suited to follow along as well. Who knows, some wizarding kids might teach you a thing or three about Ruby, and frankly, that's not a bad deal at all!

Wednesday, February 12, 2014

Book Review: Ruby Under a Microscope

Let's start with a disclaimer. I'm not a Computer Science major, nor did I complete a Computer Science course of study in school. I'm a software tester and one that finds themselves frequently using programming languages of various stripes for various purposes. Ruby is one of the most popular languages in current use, and for many, it's a language that allows them to learn some basic terms, some programming constructs, and then lets them just use it. It's clean, it's elegant, it's almost simple. It's a language that invites the user to just go with it.

For some, though, there's that sense of curiosity... what is my Ruby program really doing? How can I see what the system is actually doing with my code? What's going on underneath the hood? If such explorations interest you, then "Ruby Under a Microscope" by Pat Shaughnessy tackles that subject handily.

A word of warning going in. This is not a general language book. You will not learn much about programming in Ruby here. You should have a decent understanding of what Ruby syntax looks like and how it works. Having said that, you don't need to have years of experience with Ruby to appreciate this book for what it does. It takes some key areas of the language, and through examples, some small programs, and a variety of tools, lets you see and understand what Ruby actually looks like up close and personal.


Chapter 1 focuses on how Ruby understands the text that you type into your Ruby program. Ruby converts your source code first into tokens, and then converts that input stream into an "abstract syntax tree”. Through tools like “Ripper”, you can see this process and watch it in something resembling natural language (well, kind of. It’s still debug code, but it’s a lot less intimidating than one might think).

Chapter 2 covers how Ruby compiles code. Wait, isn’t Ruby a “scripting language, no compiler required? With 1.8 and earlier, yes, but with 1.9 and up, Ruby is compiled just like many other languages we are familiar with. The difference? Ruby does it automatically. You never need to invoke the compiler. Ruby also has its own “virtual machine” (YARV, or "Yet Another Ruby Virtual Machine) that it compiles its bytecode for. Ultimately, the byte code for YARV is what we witness running.

Chapter 3 goes into greater detail about how YARV runs our code. By comparing the steps necessary to run a simple program, we can compare the time it takes to run a program in Ruby 1.8 (which doesn’t have a compile sequence, it just runs) and Ruby 1.9 and 2.0, which do have compile sequences. For simple and brief interactions, it actually looks like Ruby 1.8 performs better, but for longer runs with more iterations, 1.9 and 2.0 have a huge advantage over 1.8 by virtue of its compile cycle. 

Chapter 4 focuses more attention on the virtual machine and how control structures and methods are handled within the YARV. If statements, for loops and calls to various methods demonstrate how ruby breaks down the instructions, as well as how it utilizes its internal calls to “jump” from one piece of code to another.  Ruby categorizes methods into 11 types, and labels its built-in methods as CFUNC methods, meaning they are implemented in C. Ruby also uses a hash to keep track of the number of arguments, her labels and what their default values should be.

Chapter 5 looks at objects and classes, specifically, Ruby’s internal objects and classes. Each Ruby object is, ultimately,  a class pointer paired with an array of instance variables, and everything in Ruby is an object. Several generic objects are shown, along with their C structures (RString, RArray, RRegexp, etc.), and demonstrate how they, likewise, are also simple combinations of a class pointer and instance variables. Classes are a little more involved. Each Ruby class can be defined as Ruby object (with its class pointer & instance variables) plus method definitions, attribute names, a constants table, and a “superclass” pointer.

Chapter 6 brings us deeper into methods and constants, specifically how these aspects are found and represented. Ruby lets a programmer look at programs with two contrasting paradigms. Our code can be organized through classes and superclasses, or it can be organized through "lexical scope". Which approach makes the most sense? it depends on what you want to have your program accomplish.

Chapter 7 gets into one of the key attributes of Ruby internals, the Hash Table. These are interesting data structures that allow a program to return values quickly, and to automatically increase in size as more elements are added. the chapter takes a deep dive into Ruby’s hash function and how it allows for elements to be accessed quickly.

 Chapter 8 covers blocks, and how the Blocks concept in Ruby borrows from the “closure” idea first prosed in the Lisp language several decades back. Blocks can be defined as “a combination of a function and an environment to use when calling that function”.  Using “lambda”,  a block can become a data value that can be passed, saved, and reused.

 Chapter 9 discusses Metaprogramming, a means to program in a way that code can inspect and change itself, dynamically. In other words, by referencing itself, your program can change itself! I’ll admit, this is one of the aspects of Ruby (or any language) that I have trouble getting my head around, and while I won’t claim to have mastery of these ideas after this chapter, I feel I have a little better feel for what’s happening.

 Chapter 10 takes us into the Java realm and shows us Ruby implemented in Java, as opposed to how we’ve been interacting with it thus far in C. The flow is similar, but each Ruby script gets compiled into a Java executable, and then is physically run by the Java Virtual Machine. We see how “Jay” parses the lines of code (much the way Bison does for MRI). By monitoring Java’s Just In time Compiler, we can see which class and structures are called whenever we create a script and run it. We can also see where, by focusing on various “hot spots in our program and compiling them into Java, we can save time in key areas compared to C implemented MRI.

Chapter 11 introduces Rubinious, a version of Ruby implemented with Ruby.  Well, it’s actually a virtual machine using C++ to run  Ruby code. What makes it different is that, rather than relying on C or Java Structures for the built-in classes, Rubinious does it with Ruby code. What does this mean? we can see how Ruby works internally without having to know C or Java. It’s all done in Ruby, and we can see how by reading the source code.

 Chapter 12 explores Ruby’s Garbage Collection, and how it differs, and is similar, in MRI, JRuby and Rubinious. Garbage collection helps us with three processes;  allocating memory for use by new objects,  identifying which objects a program is no longer using and reclaiming memory from unused objects. Various programs and examples demonstrate which objects are mapped where, and how to see when they are deallocated and their memory freed. Various algorithms for the various virtual machines are explored, but this is just a cursory overview of all the options and how they are implemented. Still, it’s an interesting view into a process that many of us take for granted because Ruby and many other languages basically let us take them for granted. 

Bottom Line:


"Ruby Under a Microscope" does something fairly ambitious. It attempts to write a system internals book in a language that non-computer scientists can readily understand. While there are numerous code snippets and examples to try and examine, the ability to look at the various Ruby internals and systems and see how they fit together can be accomplished by someone with general skills and basic familiarity with programming at the script level (which for many of us is as far as we typically get). An old saying says you can’t tell where you are going if you don’t know where you’ve been. Similarly, we can’t expect to get the most out of languages like Ruby without having a more clear idea what’s happening under the hood. It’s entirely possible to work with Ruby and never learn some of this stuff, but having a guide like "Ruby Under a Microscope” opens up a variety of avenues, and does so in a way that will make the journey interesting and, dare I say it, even a little fun.

Friday, December 21, 2012

Same As It Ever Was?

According to the Internet, and Mayan prophecy, December 21, 2012 is supposed to be the end of the world. I figure, if this proves to not be true, this will be something to allow me to celebrate a wonderful year of testing. If it does prove to be true, well, no one will be here to read this, so I suggest reading this quickly ;).

2012 was a pivotal year for me, in that I had a chance to do many things I'd never done, I had a chance to participate in a number of unique opportunities, and I made some decisions that have really made me question if it made sense to do things the same way I've done them for so long. Also, for those astute musical nerds out there, I'm referencing Talking Heads "Once in A Lifetime" once again with the title. It's proven to be quite a versatile song for these posts over the past few years.

When I wrote the first of these recaps in 2010, I was preparing to leave a job I had worked at for almost six years, and very much looking forward to a new adventure in a new capacity. In 2011, I shared many of the lessons I'd learned from making that step, and how being involved in the broader community had become very important to me. Thus I find it interesting that, here at the end of 2012, I am writing this message at yet another company, at the start of yet another "excellent adventure". "Same as it ever was?" seemed rather fitting, as this was not a year of business as usual. Not by a long shot!

2012 was a year of travel and outreach, and the opportunity to learn about and work with a number of interesting initiatives. I concluded my dive into Ruby and learning as much of the language as "Learn Ruby the Hard Way" would inspire me to do. This was a project started in 2011, and it ran for three months. I learned a lot along the way, and grew to appreciate many of the nuances of Ruby and how it works. My personal library of Ruby titles is huge now, which is a little ironic since, in my new role at Socialtext, I am looking at code that written mostly in... Perl :). Some might comment that I've wasted my time with all this Ruby focus, but I don't think so at all. What I've been able to do is approach a language at a deeper level than I ever have in the past, and do so with the eye of sharing my experience with others. That helped me internalize a lot more of it. Don't get me wrong, I'm not what I would consider a great programmer, nor even a moderately good one. Still, there's a level of appreciation and achievement I'm quite proud of, and I feel it will help me look at other languages and be a little less intimidated.

My friends Lynn McKee and Nancy Kelln invited me to participate in the POST 2012 workshop up in Calgary, Alberta, Canada, back in March. I was the facilitator for this event, and help the participants present their topics, discuss their ideas and critiques, and I also had a chance to present the idea of Weekend Testing as a service that any company could incorporate and associate with its test teams. An aded bonus, Lynn took me to Sunshine Village, one of the ski resorts in Banff National Park. Yep, I got to tick off a bucket list event... I snowboarded in Canada!

I had the pleasure of going to New Orleans to help plan out many of the events that would be part of the CAST conference for 2012, and visit some of the areas of the city (Bourbon Street, Royal Street, etc.) that I'd only heard stories about. It also gave me a chance to participate in the first Workshop day of STP-CON Spring 2012, likewise in New Orleans. I had the experience of being able to go and participate in and live blog five different workshops, and participate alongside many other intelligent and engaged testers.

I presented my first full paper and presentation at a conference this year. It was originally to be at PNSQC 2011, but a broken leg sidelined me and I was unable to present it. A friend who felt bad that I couldn't give my talk contacted Lee Copleland and suggested my talk would be a good fit for their conference. Lee read the paper and decided "yes, we'd like to see this presented" and offered me a spot on the program at STAR EAST 2012 in Orlando, Florida. I acepted and presented my talk. Additionally, I won Best Paper for "Delivering Quality, One Weekend at a Time". For the record, that was a seriously cool experience!

Weekend Testing in the Americas had a more regular schedule at one session per month. As we held our sessions throughout the year, we noticed an interesting pattern. There is a group of regular attendees that often participate. We have a number of brand new testers that come on and try it out a few times, then disappear. We have a number of one-offs, those who try it and never come back. Because of this, I determined it would be a good idea to get some additional brains into the mix to help develop sessions, content ideas, and other areas of focus. Albert Gareev has continued to be a great help to me in this regard, and we welcomed JeanAnn Harrison and Dan Gold into the mix of regularly contributing facilitators. Seriously, thank you to all of you, it made this year's sessions more enjoyable, and it gave me peace of mind to know that, if I couldn't be there for a session, that it would happen and all would be well. Additionally, I appreciate the influx of fresh ideas and different approaches.

Early in 2012, I had a chance to answer an article written about the SummerQAmp program, and what it hoped to accomplish. That proved to be a fateful message, in that I started a collaboration with the organizers of SummerQAmp and, along with members of the Association for Software Testing and other interested test professionals, we started writing what we hope will become a complete and practical introduction to the world of software testing. We delivered several modules and ran a beta test of the materials with a number of interns, and the response was "This is great! Can we have more of this?!" The answer, I hope, will be "Yes", and make no mistake, that will be a primary focus for me and for the AST EdSig in the new year. If you'd like to participate, please drop me a line!

I was invited to participate in Test Coach Camp, which happened the weekend prior to CAST 2012. This was an open-space conference event, where a number of testers participated and presented a variety of topics. I had the chance to present three different sessions (mentoring interns, teaching leadership skills, and a systematic deconstruction of Weekend Testing and the question "if we rebuilt it, what would you like to see us do?"). CAST 2012 also was the first chance to present the idea that has been my focus for much of the year, looking for that elusive balance between Test Driven Development, GUI Automation and Exploratory Testing. This topic showed up in a number of formats this year, and each time I approached it, I learned something new. First, it was a paper submission for PNSQC, then an emerging topics talk at CAST, then a full presentation at Agilistry Studios, and finally as a poster paper presentation that I gave (dozens of times) at PNSQC.

2012 saw me branch out and start contributing articles to a number of different outlets. Thanks to  ST&QA, Testing Planet, Atlassian and Zephyr for allowing me the opportunity to write for a broader audience, and for their giving me a chance to open this blog up to more testers and people interested in my writing. This year I also set a record for traffic with a post that is now number 1 with a bullet on TESTHEAD. Which post? Learning to Tell Different Stories, where I compared the storytelling tradition in Japan to what us "westerners" are used to, and how the differences and nuances open us up to asking different questions once we see and understand that there are different ways of seing things beyond our own world view. I also enjoyed participating in ST&QA's "Ask  the Tester", where I had the chance to answer a number of questions from the broader testing community. Also, I was a presenter in the Agile Transitions Online Conference for Software Test Professionals, where I presented my talk on "Being a Lone Tester on an Agile Team".

TWiST had another year of great conversations, great participation, and crossing the 100 episode mark (as of this week, we're up to episode #127). I always think of the old television maxim that, for a show to live on forever, it needs to pass 100 episodes to be eligible for life in syndication. I'm not sure if that's applicable for a podcast, but it's great to see that there is an appreciative audience, and that we can bring these discussions and ideas to you each week. I also enjoyed the various panels I participated in, and the shows I could contribute my ideas and thoughts to various discussions. Finally, I would be remiss were I not to say thank you to Justin Rohrman and Mark Tomlinson, who stepped in this year to help me edit episodes and do some of the "grunt work" that goes into getting these shows ready to be packaged and released. Seriously, your help is greatly appreciated!

During 2012, I continued my active involvement with the Miagi-do School of Software Testing, where Matt and Markus decided that I had earned the right to be advanced to a Black Belt Level Instructor. It's both gratifying and humbling to be associated with so many great testers, and while I now have the title of Instructor, sometimes I wonder who the real teacher is. I feel like I learn more from those I interact with than they likely learn from me.

As I have taken on the role as Chair of the Education Special Interest Group within the Association for Software Testing, I made the decision to step out of an active teaching role for the time being. While I will still be teaching some classes, I wanted to focus this year on giving others the opportunity to step up and learn how to lead the BBST classes and encourage those who haven't had the chance to assist and get a chance to teach as well. My goal for 2012 was to broaden our instructor pool, and that will continue to be a primary goal for 2013.

Hanging up my Lone Tester status was definitely not something I could have foreseen earlier this year, but looking at the interactions with others in so many other mediums, perhaps I should have seen it as inevitable. I decided that through all of the interactions I have had with my fellow testers, and with some feelings of frustration with my role as a lone tester, that I would put out some feelers and see if there were some test teams that would be interested in having a "Veteran of the Psychic Wars" join them. I have to admit I was surprised that so many responded, and so quickly. Thus, with the chance to "practice what I preach" regarding interaction, engagement and peer involvement, I made the decision to make the move from Sidereel, where I was a Lone Gun, to Socialtext, where I now work with a small but focused team of four testers... and by the way, we're looking for another tester to join us after the new year, so if you're interested (and local ;) ), let me know.

So many people have made this an amazing year for me, and to mention everyone by name will likely mean I'll leave someone out, so if I do, please don't feel slighted (and hey, if you do, email me and I'll put you in... blogs are cool like that :) ). Cheers and much appreciation to Aaron Scott, Albert Gareev, Anne-Marie Charrett, Becky Fiedler, Ben Simo, Benjamin Yaroch, Bill Baker, Catherine Karena, Cem Kaner, Dan Gold, Dee Ann Pizzica, Doug Hoffman, Elisabeth Hendrickson, Francis Adanza, James Bach, Janette Rovansek, JeanAnn Harrison, Jeff "Toxic" Burchell, Jon Bach, Justin Rohrman, Keith Klain, Ken Pier, Kevin Haggard, Lee Copeland, Lynn McKee, Mark Tomlinson, Markus Gaertner, Marlena Compton, Matt Barcomb, Matt Heusser, Mimi Mendenhall, Nancy Kelln, Patti Swift, Pete Walen, Peter "Pantera" Arzhintar, Rich Szeto, Rick Baucom, Scott Barber, Shampa Bannerjee, Thomas Ponnet, Timothy Coulter, and Zach Larson. Thank you for challenging me, for making me question my ideas, my motives, and my goals. Thank you for helping me make it possible to make changes, take burdens off of my shoulders and help me so that initiatives I started are being shepherded and able to keep going. Thank you for what has honestly been, at least as far as software testing is concerned, my greatest year (and remember, I said the same thing last year, and the year before that).

Oh, and should the world not end on December 21, 2012, then let me suggest that we follow the wise advice of Abraham Lincoln, who said:

"Be excellent to each other. And... PARTY ON, DUDES!"

Thursday, October 4, 2012

Codecademy Turns Ruby!

Have you seen the news? Looks like Codecademy has added a few new modules to its site. Now, in addition to learning JavaScript, Python and HTML/CSS, Ruby is now something to play with, too.

Having found Codecademy's model somewhat interesting in the past, I'm definitely interested in seeing what this system has to offer.  At first glance, it looks like they cover the basics, and get into Control Flow, Looping, Arrays and Hashes, and dealing with blocks and sorting of data. 

I sat down today and did the Introduction to Ruby, just to see how it flows, and I have to say, the folks at Codecademy have been taking notes and responding to issues that have been reported. The Intro for Ruby went very smoothly. Even when I was deliberately being stupid, it was clear enough to explain where I was going wrong and what I could do to get back on track. The information in the side bars was clear and well formatted, and the interpreters responses were likewise clear and made sense to help the user understand where they are.

The gameification aspects are kind of fun, as is the ability to brag up your completion level if you want to. Some might chuckle or denigrate the gameification; I say, if it motivates you to do more and learn more, then more power to them.

This year, I've seen Codecademy make great strides in going from a platform of interesting, yet sparse, information, to one where a sizable amount of information can be had. I wish them lots more success, eyeballs and developers willing to share. Hopefully I can get to the point to where I know enough to return the favor.

Monday, September 17, 2012

SF Ruby Book Club!

I could jump for joy about this :).

For the past several months, I've been looking at the trove of books that I have been reading, considering, looking over, and occasionally reviewing, reconsidering, and otherwise just looking at when I need them. What's missing very often is the human interaction with others. When it comes to fiction, it's easier to find people interested in talking about the books they read and what they get out oif them. Technical books are a little more challenging, because of two major issues. First, it requires a group of people be interested in the topic and the technology. Second, there are many people who don't want to engage in these discussions because, honestly, they are afraid they will be found to be technically lacking. It's like the guy who snowboards and brags about his prowess, but somehow never gets the opportunity to go up and ride with you. Or on the other hand, you invite someone and they are not the "braggadocious" type; they're just afraid that they won't be able to keep up, so they decline or refrain from participating.

Well, the San Francisco Ruby Meetup Group is starting a Ruby Book Club, and I'm all for getting into it. What's more, I'll start with the following:

"I suck at programming. I can read code, I can see what it's doing, I can even make modifications to code that already exists. Put me in front of a blank text editor, though, and say "write something", and I'm toast!"

There, that wasn't so hard!

Now I've set the expectation. I'm no great shakes at this. I feel the same about Ruby as I do about Spanish and German, two languages I studied when I was younger. I learned the rules, I learned a lot of words, I learned how to extract out of the air what I heard and read and process it and get the gist of what it meant, but I struggled with the fact that I never got good at it, and never to the point where I could converse freely. Now all these years later, I'm having the same discussion when it comes to programming, and I've decided something. I can do better.

So to those in San Francisco, I hope you'll come out and participate with me in this little adventure. It seems well suited to the goals of TESTHEAD and what I've done in the past. The first title the Book Club is considering is "The Practical Programmer", which coincidentally, is a title I recently bought out of curiosity and a desire to understand "how the other half lives". Looks like it will be more than just a curiosity now. Who knows, I may just be able to make "five new friends" (or more) in this process, and maybe, just maybe, I'll be able to get from being a bystander who hears a little here and there to one able to be part of the real conversation.

Tuesday, September 4, 2012

Book Review: ATDD By Example

ATDD is a relatively hot topic that has been getting more and more coverage both in the press and the blogosphere. I also have the benefit of knowing and have collaborated with the author of "ATDD By Example" over the past few years, so I could make this the shortest book review ever and just say "Markus Gärtner is my bud, he's awesome, his book is awesome, so go buy his book!" For those of you out there who suffer from "TL;DR", there ya' go, easy as that.

For the rest of you, you want to know what I really think, and I'm going to tell you what I really think. ATDD is a neat subject, it is a theoretical thing of beauty when it's explained at its simplest level, but what is it truly, and how does it work in a practical sense? Does it work in a practical sense? How can an everyday average tester involved in everyday testing work with this? And do I have to know Cucumber, RSpec and Ruby to have this book be worthwhile?

First and foremost, Markus explains the structure and the goals of ATDD very well. He brings his own experiences and makes examples based on things that exist in the real world, and while the examples are simple applications, generally speaking, they have enough meat to show how they actually work and demonstrate realistic issues that real developers and testers will actually face while trying to use ATDD.

Part I lets the tester follow along as Markus steps through a sample application. Many testers will chuckle when they see exactly what application he chooses; it's famous among the Weekend Testing crowd in particular; ParkCalc!!! He takes us through a very real and applicable workshop style approach, where testers, developers and the product owner determine the requirements, implement the requirements, and then create the tests, using Cucumber and Ruby for this first example. We see first steps, mistakes made, refactoring, and expansion of the application and requirements as we learn more and understand more of the domain, plus ways that we can recognize areas that we can reuse.

Part II takes us through a more elaborate example, testing the traffic light rules in Germany, this time using Java and FitNesse. By taking two different approaches and two different development environments, Markus makes the book relevant to multiple audiences, so that, instead of focusing on the tooling and the language, the reader focuses on the practices and methods used to make ATDD work.

Part III focuses on a number of topics that can help the everyday tester, developer or project manager get more out of ATDD. By stepping away from the tooling approaches of the previous two sections, Markus helps answer questions and deal with issues that are universal. Starting with developing examples to help drive the development process, as well as how to use them, format them and leverage them using pairwise testing, domain testing and boundaries, collaborating with the development team and providing testing acumen and input, making our automation as a literal analog of the requirements and specifications. In addition, taking the time to separate as much of the test details from the data that drives those tests (variables, keywords, etc.) can help make the tests we develop more robust, capable and long-lived.

Three appendices are provided, each covering basic details of three common ATDD testing frameworks; Cucumber, FitNesse, and Robot Framework. The reader will need to reference other documentation to maximize the use of these tools, but each Appendix will get the user in question up and running with the basics of all three approaches.

Beyond the examples, the main point that everyday testers will come away from this book knowing is that Acceptance Test Driven Development is Software Development, and they play a critical part in that process. If they do any type of test automation, they are developing software, and they should use the same practices, methods and methodologies that software developers use. Even if you are not specifically a coder, or you consider your skill set rudimentary, there is a lot to consider here that will help you get closer to understanding the development process and how you can contribute to it in your role as a tester.

ATDD by Example is a book that reward repeated reading. It's likely that you will get one message the first time through, and after practicing with the examples for awhile, you will give it a second pass and pick up many new things you didn't catch the first time. In short, ATDD by Example is a book that you will likely refer to on a regular basis until you get the concepts hard wired. Even then, there will be a lot of interesting tidbits that you will probably catch on as you read through it several times. Barring that, if you'd like to be more "quick on the uptake", then make sure to read Part III a few times, as it encapsulates much of the philosophy and methods that will be the most helpful to testers and developers looking to implement this approach.

Again, I could have saved you a lot of time by having you just read the first paragraph, but hey, now you know why I said it.

Thursday, August 23, 2012

Live from San Francisco, It's SFRails Meetup!



One of the things that I like a lot about San Francisco is that you cannot swing an umbrella or a computer bag within three feet of you without hitting a Meet-Up, it seems. Due to other commitments, I've been away from the fun the past few months, but it feels good to be back out and seeing what's going on again. 

That, and Sidereel's hiring Rails developers, too :). So with cards and Sidereel branded bottle openers in hand, I've made my way over to The Climate Corporation, the group giving us their space to host tonight's meet up. What's really cool is that this meet up is just a short hop from where I work. One diagonal block! I can see my building from where I'm sitting.

7:00 Pizza/Beer/Networking

We're early into the program at the moment, hanging out, eating pizza, and talking shop. I'm having a good conversation with someone who is actually already talking with my company (or has been chatting with our hiring person). I also saw that a few other friends were expected to be here tonight a well. One of the great things about these events is that it doesn't matter if you have years, weeks or just days of experience, everyone here is really cool, very chill, and like talking about what they are up to and sharing their ideas, no matter how half baked or crazy.

Stay tuned as I update this space, and I'll be happy to share what I pick up this evening. For a really quick cheat sheet of what will be covered, see below (right now, this is just a repeat of the meetup blurb. I'll be updating with my take on these topics shortly):

7:45 First Talk

GemJars: Bringing Ruby Gems to Java with Andrew Kellor (ThoughtWorks)

The first talk out the gate is from Andrew Kellor, and he's here to talk about how to bring RubyGems into Java projects. While I don't particularly use JRuby for anything, I know there are people who do (HotSpot VM as an example). the ability to have native threads, and the ability to reach into Java libraries and the JVM. the opening question to the audience... who has worked in a polyglot code base? If you have, then you can understand the frustration with trying to get the left hand and the right hand to know what each other is doing. When dealing with multiple languages, you have tooling challenges, you have interoperability issues, plus compromises that have to be made to be effective in your production code. 

So what's a GemJar? It's what it sounds like. It's Ruby Gems inside of a Java jar file. this way, all of your dependencies are in one place, and you can call them if you need them. It helped in many ways, but it also had challenges (everything in the same place means that the possibility of conflict is high). gemJars.org is doing it differently. In their world view, only the gems appropriate or a particular area (say, RSpec) in included in the jar file. I have to admit, this is outside of my everyday work, but I can see the benefit for those who do find themselves working within Java and wanting to have some of the cool tools and ease of doing some things that Ruby allows. 

8:15 Second Talk

Maintainable CSS with Sass and Bourbon with Jonathan Mumm (TokBox)

This talk covered a number of tools and techniques so that the developer could make sure that they were doing as much as they could to keep CSS from becoming cluttered, repetitive and difficult to maintain. By demonstrating practices (good one in context ;) ) using SASS and a Ruby gem called Bourbon), the programmer can make sure that they are taking advantage of the options that both SASS and Bourbon provide so that code is uncluttered and not repetitive. 

Since I lost the network for a bit, i wasn't able to jot down these details, but there's some cool resources to get to know how to tweak these options, including the SASS Rails gem and the Bourbon Gem documentation options. The ultimate goal is to make it so that you can make your code clean, orthogonal, with minimal duplication (yes, I know that's part of what orthogonal means ;) ).

All in all, a pretty good night. A little bit of enterprise level stuff mixed in with some rudimentary details, so there was truly something for everyone. Also, the pizza was great and the company, as alwayys, was terrific. Thanks for a great night everyone. Til next time!





Wednesday, June 20, 2012

My Current Dilemma: Resolved (Sorta)!

Yesterday, I mentioned that i had some issues with making my tests for Cucumber be totally random if I wanted them to be. I also asked the test community out there to chime in with their thoughts and their approaches to how to handle this. I received a number of replies through Twitter and email, and it even got the attention of one of my team's developers, who when I told him what I was aiming to do, did some digging of his own... and here's what he found:



Basically, if you add this to your env.rb file, it will shuffle your feature file.

I've tested this, and it does indeed work. No tweaks, no file name or directory changes needed. The only thing is that it's either on or off, so my next step is to see what it will take to set this as a flag to rake. It would be sweet to just say something like "

rake cucumber:tag:machine:random


To have randomized runs, and not include the random tag to have them run in standard, alphabetical order.


I'm not 100% of the way there, but this is pretty good so far :).

Thursday, March 29, 2012

RESTful Bundling - An #sfrails meetup followup

It's been awhile since I've been able to get out to a San Francisco Rails Group meetup, so I'm happy to be out tonight at Plum District, the company who is hosting what looks to be a very full and standing room only group (glad I got here a little early :) ). Not to be outdone, we even had a emergency alarm system go off during introductions. talk about a warm welcome!


Tonight's two talks were focused around Bundler and developing RESTful clients. Andre Arko (@indirect) led off the evening by discussing Bundler and where it came from, why it's here and where it is going. For those familiar with Ruby (and those who are not) libraries and compiled code modules are available through an  interface referred to as Ruby "Gems". Trying to keep track of all of the gems needed, making sure that the exceptions didn't derail your progress, and trying to figure out which gems your code actually uses.


Before bundler, there were various ways to try to solve this problem, some of which were quite painful and frustrating (lots of "gem install" in script files, etc.). Bundler solves this  problem by making sure that gems and their ordering are handled automatically, so that you are able to make sure you are running what you think you are running.


Today, Andre talked about Bunder 1.1 and some of the new features it has, such as github specific gemfiles (kinda cool :) ). You can now easily check to see if your gems are outdated. You can clean up old gems and remove the ones you are no longer using. Bundler 1.1 now supports subshell without bundler, and bundler now shows the absolute path to every gem in your bundle if you want to search for them. The big question, though, is "IS IT FASTER?!" The answer is "yes", by about a factor of four, but don't take my word for it, download it, install it, and check it out for yourself :).


Bunder 1.1 is a lot better, and they want Bundler 1.2 to be even better than that. To do that, the goal is shorter release cycles, creating ruby version check systems, and checking out local gems if set up to do so. One of the big goals is to get rid of "bundle exec". Overall, this looks cool and I'm interested in seing what we'll be offered down the road.


The second talk was focused on developing ReSTful clients, and explaining how everything fits together. Jack Lawson from Plum District talked about some of the chalenges he faced while implementing an API to build out a client, any client. These areas work for websites, mobile apps, tablets, etc. I have to admit, much of this is still fairly esoteric to me, but since I've worked through Learn Ruby the Hard Way, a lot of these conversations make a lot more sense now. I still have some holes to fill in, but they feel much less massive than in previous meetups I've attended. Building the API is just the first half of the battle. Actually getting everything to play nice is the other half. Backbone, CoffeeScript, and Mustache / Hogan.js were demonstrated so that we could see the steps necessary to progressively grow the client application. Pretty cool all told, though still a bit over my head in spots.

I'd be remiss to not say a hearty thanks to everyone who puts on these Meetups at regular intervals. We comment often that you can't swing an umbrella in San Francisco without hitting some kind of meetup. There's a wealth of talent and opportunity here. I need to remember to  not take such things for granted.

Tuesday, January 31, 2012

Exercise 51: Getting Input From A Browser: Learn Ruby the Hard Way: Practicum

All right, so we have a simple Sinatra framework up and running. Now let's see if we can do something just a little more exciting, shall we :)?


This time around, we'll make some interactive elements. We will submit text to our application by using that well known method, the form. Additionally, we'll look at ways that we can do automated testing for forms.




How The Web Works (The Boring Bits)




Zed's got this pretty well nailed down, so I'll let him tell this part :):

- You type in the url http://learnpythonthehardway.org/ into your browser and it sends the request out on line (A) to your computer's network interface.


- Your request goes out over the internet on line (B) and then to the remote computer on line (C) where my server accepts the request.


- Once my computer accepts it, my web application gets it on line (D), and my web application code runs the / (index) handler.


- The response comes out of my web server when I return it, and goes back to your browser over line (D) again.


- The server running this site takes the response off line (D) then sends it back over the internet on line (C).


- The response from the server then comes off the internet on line (B), and your computer's network interface hands it to your browser on line (A).


- Finally, your browser then displays the response.

Rather than go through a full breakdown of what the web terms mean, I'll let the reader take a look at Zed's very well done crash course explanation over at http://ruby.learncodethehardway.org/book/ex51.html (chances are, if you've gotten this far with me, you already know the links and site very well.

How Forms Work

Let's take the lib/gothonweb.rb file make some changes to it:


- Restart Sinatra (hit CTRL-C and then run it again) to make sure it loads again



- With your browser go to http://localhost:4567/hello which should display, "I just wanted to say Hello, Nobody."



- Next, change the URL in your browser to http://localhost:4567/hello?name=Frank and you'll see it say "Hello, Frank."



Finally, change the name=Frank part to be your name. Now it's saying hello to you.



So what have we done here?


- We're now using the "params" hash with a value of ":name" to get data from the browser. Sinatra takes all of the key/value pairs after the ? part of the URL and adds them to the params hash for you to work with.

- The greeting is then constructed from the value of ":name". By default we set this to "Nobody". If we give it a name value on the command line, it changes it based on the "name=EnteredName" that we put in the URL after the "?"

We can add more than one param on the command line, too if we modify the script to accept the changes.


We can change the code to get params[:name] and params[:greet] as well like this:



By default, it looks like this:



And with the url values, it looks like this:

Example:
http://localhost:4567/hello?greet=Wassup&name=Michael




Creating HTML Forms


OK, so we can pass the values through the URL, but let's face it, that's a pain. Most people expect to enter information in the browser directly and hit a button. this uses the time honored web feature called a form (or a POST form, to be more specific). A form is just an HTML file with a "form" tag in it. This form will collect information from the user, then send it to your web application just like you did above.

Let's make a quick form to see how this works (this is being done in  lib/views/hello_form.erb):


Now make some changes to gothonweb to be able to accept the form:


Once you've got those written up, simply restart the web application again and hit it with your browser like before.




The part of the hello_form.erb file that makes this work is the line with
. This tells your browser to:

- Collect data from the user using the form fields inside the form.

- Send them to the server using a POST type of request, which is just another browser request that "hides" the form fields.

- Send that to the /hello URL (as shown in the action="/hello" part).


Creating A Layout Template

For the final exercise, we'll be making a bunch of small HTML pages. Having to always code up a page each time will soon become tedious, so we'll create a simple "layout" template to wrap all of our pages with common headers and footers.

So here's the change to lib/views/index.erb:



Change lib/views/hello_form.erb to be like this:



This removes all of the general "boilerplate stuff" that every page will have at the top and the bottom, and now we'll make a layout page that contains all of that:

Here's lib/views/layout.erb file that handles it for us from now on.

Once you have those changes, create a lib/views/layout.erb file with this in it:




Writing Automated Tests For Forms

So now that we have made these changes, we could keep loading up the web page each time to see if we have made the correct changes... or we could use our unit tests to see if what we have put in works, too. The tester in me would like to see us do both, so lets do that :).

Create the file test/test_gothonweb.rb with these contents:



Finally, run test/test_gothonweb.rb to test your web application:

$ ruby test/test_gothonweb.rb
Loaded suite test/test_gothonweb
Started
.
Finished in 0.023839 seconds.


1 tests, 9 assertions, 0 failures, 0 errors, 0 skips


Test run options: --seed 57414

Note: what's being seen here is not what I am seeing. Maybe there is a step missing, but the framework appears to be running as expected. Again, it's a problem I'l tweak a bit more later.

Zed explains that what is happening here is that we're importing the whole application from the lib/gothonweb.rb library, then running it manually.

The rack/test library we have included has a very simple API for processing requests. Its get, put, post, delete, and head methods simulate the respective type of request on the application.

- All mock request methods have the same argument signature:

get '/path', params={}, rack_env={}

- /path is the request path and may optionally include a query string.

- params is a Hash of query/post parameters, a String request body, or nil.

- rack_env is a Hash of Rack environment values. This can be used to set request headers and other request related information, such as session data.

This works without running an actual web server so you can do tests with automated tests and also use your browser to test a running server.

To validate responses from this function, use the assert_response function from test/test_gothonweb.rb which has:

assert_response(resp, contains=nil, matches=nil, headers=nil, status=200)


TESTHEAD's TAKEAWAYS:

This is pretty cool, again, this helps make a lot more sense out of what I've been seeing in the code and the rails applications that I use (the boilerplate loading up the header and the footer that's the same, and then the specific code elements being loaded as needed and only when needed, it's a beautiful thing. The Unit tests are not behaving the way that I expect them, to, but again, it's possible I'm missing something or I configured something wrong. Even without this specific aspect, I think the point has been made. Sinatra makes for an elegant little framework for running a server.

Monday, January 30, 2012

Exercise 50: Your First Website: Learn Ruby the Hard Way: Practicum

Cool, we have finally reached the point where we interact with the web and web components using Ruby.


The example text uses the Sinatra framework.


Having heard of Sinatra in various places, I've been interested in seeing how to interact with it (and see how it differs from Rails, which is an environment I already use and test in my "day job" :).


Installing Sinatra


Using gem install Sinatra:



Make A Simple "Hello World" Project


Now you're going to make an initial very simple "Hello World" web application and project directory using Sinatra. First, make your project directory, and then create the structure by running "bundle gem gothanweb (gothan being the game we've been working with for the past while):

Now let's make a very basic Sinatra application by putting the following code into lib/gothonweb.rb:
Then run the application like this:


Finally, use your web browser and go to the URL http://localhost:4567/.

Well, this is what I see:



What's Going On?

Here's what's happening when your browser hits your application:

- The browser makes a network connection to your own computer on port 4567.

- Once it connects, it makes an HTTP request to the lib/gothonweb.rb application and asks for the / URL, which is commonly the first URL on any website.

- Inside lib/gothonweb.rb there are blocks of code that map to URLs. The only one we have is the '/' mapping.

- Sinatra calls the matching block, which simply returns a string for what Sinatra should send to the browser.

- Finally, Sinatra has handled the request and sends this response to the browser which is what you are seeing.


Fixing Errors

Comment out line 6 where you assign the greeting variable, then hit refresh in your browser. 



You should see an error page now that gives you lots of information on how your application just exploded. You know that the variable greeting is now missing, but Sinatra gives you this nice error page to track down exactly where. Do each of the following with this page:

- Look at the sinatra.error variable.

- Look at the REQUEST_ variables and see if they match anything you're already familiar with. 

- Uncomment line 6 and all returns back to normal.





Create Basic Templates





The following steps will make a properly formed "web page". This page used the embedded ruby extension [filename.erb]. It's stored in lib/views.

The first step is to create a lib/views/index.erb file that looks like this:
Now we modifying the lib/gothonweb.rb script, so that Sinatra has a way to access the ERB file:


So what happens if we reload the web page with these changes?

And just to show that it's properly formsed HTML...:
So what's going on here?

- In lib/gothonweb.rb we added an erb method call.

- The erb method loads .erb files out of the lib/views/ directory. We pass it index.erb because we're passing that as a parameter (erb :index ...).

- Now, when the browser hits / and lib/gothonweb.rb matches and executes the get '/' do block, instead of just returning the string greeting, it calls erb and pass greeting to it as a variable.

- Finally, the HTML in lib/views/index.erb contains Ruby code that tests the greeting variable, and prints a message using the greeting.


TESTHEAD's TAKEAWAYS:

So this is a very simple and basic example, but it shows that we can fairly quickly develop a web site within a framework and use Ruby to pass variables for the values it would expect to see. That's pretty cool :).





Exercise 49: Making Sentences: Learn Ruby the Hard Way: Practicum

For the record, here's the point where people get moderately bent at programming books when they can't seem to figure something out. Again, this is my problem, not Zed or Rob's, but it does point out what often comes to be a challenge for many.


When you get stuck on a concept, oftentimes, there really isn't a way to move forward until you figure it out. While I don't want to get all the way through the book and decide I'm stranded on one point, two exercises call for me to have my act together on this functionality (which makes perfect sense, because in life, you have to make the system work before you can add new features.


Thus, I'm reviewing this chapter with a significant handicap; I'm still stuck from Exercise 48.

So if we have managed to get the lexicon scanner to work, we should see output that looks something like this:



ruby-1.9.2-p180 :003 > print Lexicon.scan("go north")
[#<struct Lexicon::Pair token=:verb, word="go">,
    #<struct Lexicon::Pair token=:direction, word="north">] => nil
ruby-1.9.2-p180 :004 > print Lexicon.scan("kill the princess")
[#<struct Lexicon::Pair token=:verb, word="kill">,
    #<struct Lexicon::Pair token=:stop, word="the">,
    #<struct Lexicon::Pair token=:noun, word="princess">] => nil
ruby-1.9.2-p180 :005 > print Lexicon.scan("eat the bear")
[#<struct Lexicon::Pair token=:verb, word="eat">,
    #<struct Lexicon::Pair token=:stop, word="the">,
    #<struct Lexicon::Pair token=:noun, word="bear">] => nil
ruby-1.9.2-p180 :006 > print Lexicon.scan("open the door and smack the bear in the nose")
[#<struct Lexicon::Pair token=:error, word="open">,
    #<struct Lexicon::Pair token=:stop, word="the">,
    #<struct Lexicon::Pair token=:noun, word="door">,
    #<struct Lexicon::Pair token=:error, word="and">,
    #<struct Lexicon::Pair token=:error, word="smack">,
    #<struct Lexicon::Pair token=:stop, word="the">,
    #<struct Lexicon::Pair token=:noun, word="bear">,
    #<struct Lexicon::Pair token=:stop, word="in">,
    #<struct Lexicon::Pair token=:stop, word="the">,
    #<struct Lexicon::Pair token=:error, word="nose">] => nil
ruby-1.9.2-p180 :007 >


With this output, we should be able to now take the token pairs and make an actual sentence, using a Sentence class.

Sentences can be structured simply by combining the following tokens (for English, anyway; different languages will have different rules):

Subject Verb Object

So the primary goal of the sentence class is to turn the lists of structs above into a Sentence object with a subject, verb, and object.

Match And Peek

To do this we need four tools:

- A way to loop through the list of structs.

- A way to "match" different types of structs that we expect in our Subject Verb Object setup.
- A way to "peek" at a potential struct so we can make some decisions.
- A way to "skip" things we do not care about, like stop words.
- We use the peek function to say look at the next element in our struct array, and then match to take one off and work with it.

So here is the first peek function:

def peek(word_list)
  begin
    word_list.first.token
  rescue
    nil
  end
end

This is the match function:

def match(word_list, expecting)
  begin
    word = word_list.shift


    if word.token == expecting
      word
    else
      nil
    end
  rescue
     nil
  end
end

This is the skip function:

def skip(word_list, word_type)
  while peek(word_list) == word_type
    match(word_list, word_type)
  end
end


The Sentence Grammar

To build our Sentence objects from the struct array, we can do the following:

- Identify the next word with peek.

- If that word fits the grammar, call a function to handle that part of the grammar

- If it doesn't, raise an error (see below).

When we're all done, we should have a Sentence object to work with in our game.

So this time, instead of being given the test and trying to figure out the code, this time, we get the code and we then figure out how to write the test to meet the requirements.

Here's the code for parsing simple sentences using the ex48 Lexicon class:



The sections below are advice given by Zed and Rob. It's printed in italic to show it's their words verbatim.

A Word On Modules

This code uses something in Ruby called a "module" named Parser. A module (created with module Parser) is a way to package up the functions so that they don't conflict with other parts of Ruby. In Ruby 1.9 there was a change to the testing system that created a skip method which conflicted with the Parser.skip method. The solution was to do what you see here and wrap all the functions in this module.


You use a module by simply calling functions on it with the . operator, similar to an object you've made. In this case if you wanted to call the parse_verb() function you'd write Parser.parse_verb(). You'll see a demonstration of this when I give you a sample unit test.


What You Should Test

For Exercise 49 is write a complete test that confirms everything in this code is working. That includes making exceptions happen by giving it bad sentences. Here is a starter sample so you can see how you would call a function in a module:

require 'test/unit'
require_relative '../lib/ex49'


class ParserTests &lt; Test::Unit::TestCase


    def test_parse_verb()
        # WARNING: THIS FAILS ON PURPOSE SEE THE BOOK
        Parser.parse_verb([false])
    end


end

You can see I make the basic test class, then create a test_parse_verb to test out the Parser.parse_verb function. I don't want to do the work for you, so I've made this fail on purpose. This shows you how to use the Parser module and call functions on it, and you should work on making this test actually test all the code.


Check for an exception by using the function assert_raise from the Test::Unit documentation. Learn how to use this so you can write a test that is expected to fail, which is very important in testing. Learn about this function (and others) by reading the Test::Unit documentation.


When you are done, you should know how this bit of code works, and how to write a test for other people's code even if they do not want you to. Trust me, it's a very handy skill to have.

Extra Credit

-  Change the parse_ methods and try to put them into a class rather than be just methods. Which design do you like better?
- Make the parser more error resistant so that you can avoid annoying your users if they type words your lexicon doesn't understand.
- Improve the grammar by handling more things like numbers.
- Think about how you might use this Sentence class in your game to do more fun things with a user's input.