Last week I was looking around for a ORM (Object-Relational Mapping) library for PHP. I started my search from the ORM page at Wikipedia, which took me to several articles that compares several MVC (Model-View-Controller) frameworks. These frameworks are designed after Ruby's famous Rails. I decided to try CakePHP because it receives numerous good reviews. Symfony also receives good reviews, but I had tried it before, and it wasn't really what I was looking for.
Soon I realized that CakePHP will not work for us too. Being a framework that was designed with "Convention over Configuration" paradigm in mind, it doesn't fit in a large existing environment that was built using a different sets of conventions. It was a pity, it seems that CakePHP is a good framework, it just came too late, at least for us. It is unrealistic for us to redesign everything that we have to adapt to CakePHP/Rails conventions just to adopt the framework.
The findings brought me back to my initial intention, maybe I can just find a good ORM library to be used for the Model. We would then fill up our own Controller and View handlers.
Pretty soon, I came across to ADOdb's native Active Record implementation! I was overjoyed, while slightly cautious, when I discovered it. We have been using an older version of ADOdb, and didn't realize that the new one comes with an Active Record implementation. An ADOdb native implementation means that we don't have to install a brand new library in our servers. We just need to upgrade what we have to the latest version. However, I was cautious that it enforces too many restrictions that we couldn't live with.
After reading the manual and trying to build several simple pages with it (at that time it was ADOdb 481), I had good confidence that we can use it for our new applications. It is not a complete implementation, as a few things are missing. But the most important thing is that it doesn't strictly corner me with conventions that I didn't make.
One missing feature that I am quite fond of was a factory method: "Find", that finds records that match your criteria, and returns the record objects for you. I was thinking about writing one, and contributing it back to ADOdb. I mentioned my inquiries in the forum.
To my surprise, John Lim, ADOdb's creator responded to my inquiries the next day with a new release of ADOdb (version 490) that includes a Find factory in it. Beat that for responsiveness to feature request!
I am now a happy user of ADOdb and ADOdb Active Record.
For those of you who are considering of using it, here is a short list of the goods and the bads of ADOdb's Active Record:
I am writing this blog on a pleasant United Airlines flight from San Francisco to Chicago. This time I am going to spend some time in the windy city for the first International Rails Conference (RailsConf 2006). Though I am excited about the conference, and anxious to be in Chicago one more time, I am writing about something that has nothing to do with Ruby or Rails or technology in any flavors. Instead, I am writing about a soda drink, an common, flavorful, and contradictory ginger ale.
During the drink service, I ordered one soda drink that I like more than other sodas, the ginger ale. The stewardess gave me a can of Canada Dry, and a glass of ice.
Halfway through the can, I recalled one nice lunch that I had in a restaurant called the "Brick House" in Goshen, Indiana. I ordered the ginger ale, and the waiter politely told me that they did not have any. He suggested their version of ginger ale instead, made from a perfect mix of Coca-Cola and Sprite. I tried it, and it tasted quite nice. Very close to the real thing.
I replayed that moment in my head, and then thought, "... is there anything ginger in ginger ale? ... How can it be imitated by mixing caramelized and lemonized soda waters?" So I looked at my Canada Dry can, and read it thoroughly. I was then convinced that Canada Dry ginger ale is the most oxymoron soda drink ever. The reason, the four words that make up it's name tell us exactly what it is not. Here is why:
There you have it. If the very name that is used to describe something tells you what it isn't, how can it be more oxymoronic. Maybe we should start calling it "unCanada Dry unGinger Ale."
In the first talk in the first day of the first International Rails Conference, Dave Thomas graced the Rails alpha adopters with a keynote exposing several areas that have to be solved for Rails to move forward. He playfully, but also in respectful and solemn way, mimicked David Hilbert's address to the Mathematical world at the turn of the last century. Hilbert put forward 23 (actually, initially only 10) problems that had to be addressed by Mathematicians for the next 100 years.
In this occasion, Dave exposed only 3 problems to be solved hopefully sooner than 100 years. Though only 3, these problems are not trivial. These problems cover areas in Rails that are not well developed yet that are currently asked by the corporate world. Dave said that these problems, if addressed right, determine the adoption of Rails to the masses.
These problems are technical ones, covering better data support (model), better out of the box CRUD (scaffolding), and better deployment model. You can find good recaps in here and in here.
I would like to add my perspective on a few other challenges that Rails may encounter as it moves forward. These are not technical problems, but still in no way trivial.
These problems are not exclusive to Rails, or to any technologies for that matter. It applies with any anything that is growing. It is the same problem that J.K Rowling had to face when her books grew into movies. These are the growing pains. These problem centralized into one troublesome fact: "As anything grows, there are new owners and stakeholders. The new owners may have conflicting needs and ideologies with the early adopters." These problems are the balancing act of keeping control and letting go of it. If they are not addressed properly, they can be traumatic. So let's hash them out.
1. Ideological Challenges:
Rails was created with a set of powerful ideologies. Some of which are idioms like "Convention over Configuration," or "Don't Repeat Yourself," which are ingrained to the core of Rails. As Rails grow, there will be occasions where these ideologies are going to be challenged. Even Dave's in his keynote mentioned numerous times the words "additional configurations", "more flexibility" and others. These requirements may directly conflict with the ideologies that Rails were built upon.
David H. Hansson often quoted saying that Rails was an opinionated software, it was never meant to please everyone. That should remain true, but in order to grow, Rails need to please more and more people.
2. Community Shepherding:
Alpha adopters are by definition smarter, braver and more innovative. It is pleasing to work with these people. They complain less, and contribute more. However, as Rails gets adopted by the masses, it looses the luxury of having alpha-only community. Late adopters need more hand-holding, and they fall into the same problems again and again. The challenge is then for the alpha adopters to be patient and to be helpful so that late adopters can quickly help themselves. Better and more accessible documentations, forums and samples are compulsory. One good metric to measure this problem is to observe the frequency of the usage of the acronym "RTFM" in the mailing list.
3. Herding the cats:
Rails core team as of today consist of 12 people. As Rails grows, users will try to extend and stretch it to all possible directions, beyond the expertise of the core team. The challenge is then for the core team to manage this requests and suggestions. It may not be trivial, especially if Rails popularity grows very fast.
So here are three non technical issues that need to be addressed. The list is by no mean exhaustive, but I want at least to do my part in bringing this issues to the surface.
It hasn't been a day since I put forward several non-technical issues that can loom the growth of Rails. Just a few minutes ago David Heinemeier Hansson already dismiss some of those concerns in his 2nd day railsconf keynote. Responding to the pressure of making RAILS conform to the real world, he insisted that Rails should not bend over to it. He pointed out that so far the real world has actually began submitting to RAILS conventions. He made it clear that he wants to keep it that way. The conventions are good and correct, and RAILS is going to move forward with them.
To a certain extend, his keynote may seems like a rebuttal to Dave Thomas' keynote yesterday, which laid down paths towards Rails acceptance in the enterprises. This dichotomy is certainly not a hidden in RAILS community. On this very day, in the same conference, we had talks like 'Using iBatis to Support Legacy Schema' and 'Using RAILS in the Enterprise.' However, I am glad that DHH is not a practitioner that was tamed by years of experience in working for an enterprise. His grassroots upbringing allowed him to be a purist and to think beyond the corporate works. If the whole world does bend over Rails conventions, then things may be really interesting in the future.
Here are some of the points that DHH delivered today.
1. Rely more on simple CRUD (CREATE, READ, UPDATE, DELETE) operations.
DHH noticed that people often extend controllers with many methods to deal with secondary entities that are associated with the primary ones. These practices generally occur when developers handle several entities associated in many-to-many relationships in the same controller.
To describe this point, he examined an example from a common practices of associating 'user' entity in a many-to-many relationship with 'group' entity. Developers often code the responsibility of managing the relationship table either in the user controller, or in the group controller. He suggested that developers should simply treat the relationship as a separate entity, call it 'membership'. This 'membership' entity can now have it's own controller, and everything controllers need to worry about go back to simple CRUD again.
(Note: I have one simple question with this. What if the associative entity name is not nicely pluralized? How do you pluralized names like 'entry_tagged?' This is, of course a minor issue...)
2. Simply RESTful: tightly map CRUD with HTTP's: POST, GET, PUT, DELETE.
DHH began with mentioning the commonly understood coupling between the CRUD operations of the controller world to subsequent INSERT, SELECT, UPDATE, and DELETE operations of the model/sql world. He then mentioned the four HTTP protocol operations (POST, GET, PUT, DELETE), and showed that they can actually be tightly coupled with CRUD as well.
This three way coupling then create the following operational mapping (HTTP -> Controller -> Model):
These four mappings also bring the side effect of eliminating the need to pass the actions into the request URLs. Instead of sending a POST to /users/update/12 to tell the server to update user record with id 12, browser should simply send a PUT to /users/12. The same shortened URL should also respond to GET and DELETE requests and perform READ and DELETE to the record. A CREATE can then be done by sending a POST to an even shorter URL: /users.
3. Use the same controller to respond to several MIME types.
Traditionally developers often use different controllers (consequently different URLs) to process requests for differing MIME types; for example HTML, or XML for the many flavors of RSS. Apart from the inconsistent URLs to access data from the same entities, the additional controllers also incompatible with RAILS DRY (Don't Repeat Yourself) philosophy.
DHH urged that there should be only one controller used to process one entity. This is possible because the request MIME types are already known by the controller from the HTTP header.
4. Type specific requests should use name extension.
However, HTTP request headers are not always reliable, due to mistakes done by the available client software. DHH pointed out that most RSS readers send requests with headers containing MIME type HTML, while actually wanting XML back. In this case, there should be a way to override the request type in the URL itself. DHH suggested the use of file extension to do so. A GET request to /users.xml/12 will be routed to the users controller, and it will return attributes of user object 12 in XML format.
5. Active Resource.
Realizing that models are not always database backed, DHH proposes the creation of a package called the Active Resource. Active Resource should behave more or less like Active Record, but it should support other backends like LDAP or file system. However, Active Resource may not be part of core RAILS.
6. Additional words about conventions.
All over the place, DHH asserted that people should follow the conventions. He added that enforcing conventions do not mean closing the possibility for others to do differently. However these other possibilities should not be made easy. Implying that not following the best practices was insanity, DHH said, "If you want to indulge in insanity, it should HURT."
Once again I want to mention that I am glad David Hansson worries less about adaption of RAILS in the enterprises, and focuses on the way web development should be. This way the philosophical issues I raised yesterday are automatically dismissed (the community and governance issues persist, though less challenging).