In a previous post I talked about learning Rails in a way that I wished it had been presented to me. Here I’ll focus a little on the salient facts about the framework that you will no doubt find elsewhere, but sometimes you have to distill it from surrounding material that gets in the way. Near the end, I’ll explain why any of this has relevance to me writing as a tester.
An interesting thing about Rails is that it was extracted from a real-world, commercial application. The details really don’t matter but the practical upshot is that because of this history when you develop your own application with Rails, you are starting with a large part of your application already in place. Yet because your application is slotting into an existing application, that tends to mean that, right out of the gate, there’s a defined place for each piece of code you write. After all, that’s how slotting tends to work. Further, since you are writing code into an existing application, that means that all the pieces of your application interact in a (mostly) standard way.
The Rails Idioms
A lot is made of the various principles, concepts, and practices of Rails, so let me add my own two cents to this. There are a few key elements to understand about the basis of Rails.
- Rails follows the idea of the principle of least surprise (POLS).
- Rails follows the principle of Don’t Repeat Yourself (DRY).
- Rails follows the practice of convention over configuration.
- Rails follows the philosophy of You Ain’t Gonna Need It (YAGNI).
- Rails follows the concept of rapid prototyping.
So what does all this mean?
The POLS idea really just means that things (ideally) behave in a predictable manner that is (mostly) easy-to-decipher. The realistic caveat with this is that you do have to have a good understanding of how things work so that you can leverage the predicable and easily-decipherable aspects. If that sounds like I’m contradicting a bit, well, welcome to Rails.
It may sound like I’m being a bit flippant there but, in all honesty, it really does pay to keep what I just said in perspective. Rails does a lot for you “behind the scenes” and abstracts away a lot of details. That can make your life easier. It can also make how things work very opaque.
Don’t Repeat Yourself is such an oft-quoted principle, I probably don’t even need to spend time on it. It’s really fairly simple. In essence, it states that information (every piece of knowledge) in a system should be expressed in only one place. As a practical example, Rails keeps all of its database access information in a central location, which is a database.yml file. The nature by which this is done also ties in to the POLS idea because that data is stored in plain text: so you know exactly where it is and exactly what it says. (And as long as you understand how Rails uses it, you know exactly how it works.)
Artifacts like the database.yml file lead into the next element: the practice of convention over configuration. In frameworks that are “opinionated”, which usually means they have some focus on convention over configuration, “doing the right thing” is made (ideally) easier by virtue of the fact that “doing the wrong thing” tends to be a bit harder. So, for example, if you want to make your application utilize databases in a way not based on the database.yml file, you can do that … but you’re on your own. So convention over configuration means that you need to define only configuration that is unconventional. But when you follow the conventions, you will find (usually) that there are intelligent defaults for nearly every aspect of the framework. This frees you up from having to explicitly tell the framework how to behave, such as how to access databases.
Again, all of that can be really good and you can count on it for a lot. Just keep in mind the caveat: the more you rely on the “stuff behind the scenes” without knowing exactly how it works, the more you are dependent on everything going just fine and your application not needing to color outside the lines, as it were.
With convention over configuration, you rely on Rails having sensible defaults for just about every aspect of knitting together your application. Follow the conventions, and you can create a Rails application using less code. (Less code that you see and directly write, that is; there can be a lot of code that you generate as opposed to actually write.) If you need to override the conventions, Rails makes that (relatively) easy as well. With DRY, you write all the convention stuff you need to code in one place. That place being suggested by the particular conventions of the MVC architecture upon which Rails builds.
As far as YAGNI, this really isn’t something that Rails “builds in” so to speak, but the conventions aspect sort of helps you adhere to it. The idea being that if Rails didn’t build something into its conventions, you might want to consider whether that something is really needed. Rails provides you spots for the common artifacts you will require for an MVC type web application. It provides mechanisms behind the scenes that knit all of the MVC components together. What that theoretically does is leave you just coding up your specific application’s needs in various views, controllers, and models. Anything else you don’t (or might not) need.
Finally, regarding rapid prototyping, the main way that Rails allows this is by adhering to all of the other items in the list. When combined those items allow you to close the gap between the customer who wants a solution and the developers who are providing it. A primary focus here is the ability to leverage Rails to make changes to your application easily as the customer and developer, working together, learn more. This means that Rails development allows you to postpone decisions in anticipation of likely — and perhaps even unlikely — change.
The Rails Artifacts
Here I want to give a view of the Rails artifact system, largely in what I consider a concise way and particularly in the way I wish I had learned it.
Everything in Rails is predicated upon the MVC architecture. This architecture promotes the idea of separation of concerns so that each element of the architecture is independently testable.
In Rails, the model is responsible for holding the data that the application relies on but also maintaining the state of the application by protecting that data from being used in ways it should not. Models encode and enforce all the business rules that apply to that data.
So, putting this in context, Rails applications are usually made up of several individual models, each of which (usually) maps to a database table. For example, a model called Planet may map to a table called planets. The Planet model assumes responsibility for all access to the planets table in the database, including creating, reading, updating, and deleting rows. The important thing to remember is that models represent data. All rules for data access, associations, validations, calculations, and routines that should be executed before and after various operations (like save, update, or destroy) are neatly encapsulated in the model.
In Rails, the view is responsible for generating a user interface, normally based on data in the model. That’s fairly simple.
In Rails, controllers orchestrate the application. This is where everything comes together. Controllers will receive events from an outside source, such as a web service or web browser. Controllers will interact with the model in order to gather data, often based on those events. Controllers will handle displaying an appropriate view to the user, making sure the view has access to the relevant data from the model. In Rails, helpers provide a way to bridge the gap between controllers and views so that they can communicate but still maintain separation.
Putting all this another way and more concisely:
- Controllers generally perform a request from the user to create, read, update, or delete a model object.
- Models contain a representation of the data that passes through your application.
- Views have the responsibility of formatting and presenting model objects for output on some device.
- Views will also provide the mechanisms, such as input forms, that accept model data.
When you are creating a solution in Rails, you develop models, views, and controllers as separate chunks of functionality. Rails, acting as a framework, knits those chunks together as your application executes, based on events received from users. As mentioned earlier in the principles that Rails adheres to, this knitting is (ideally) based on intelligent and intelligible defaults so that you, as an application provider, need little configuration and do not need to spend much time mired in metadata. (If you’ve worked with Java-based web frameworks, you now what being “mired in metadata” means.)
Another aspect of Rails that is a direct output of its design is that all of the separate chunks of functionality can be generated to give you a head start in writing your application. Not only that, but Rails will also generate test functionality for each of those chunks. However, to be more specific not to mention accurate, Rails creates test stubs for all of your functionality. In theory that makes it easy to test your Rails applications and this is often touted by Rails pundits. However, do note that just because Rails test stubs are generated, this does not mean those stubs have to actually be used. And even if they are, that doesn’t necessarily mean that a Rails application is being tested well.
As a tester …
Why did I spend time worrying about all this? Why did I take time to actively distill the basis and philosophy of Rails? Mainly because, by doing so, it helps me understand not only how developers use the framework but how developers think about the framework. (This same thinking translates well to other frameworks, like Python’s Django.)
This in turn has forced me to understand some aspects of Rails that guide (or misguide) thinking. This has allowed me to understand weak points in the framework by better understanding where things can fall through the cracks, as it were, particularly as Rails sits there “magically” knitting together the parts of my web application.
In short, understanding Rails like this combined with playing around with it, has not only made me a better tester of Rails-based applications but has guided me in terms of how to evaluate and test other, similar frameworks.