Back in the day I tried Ruby on Rails for web development, but got turned off by the heavy dependence on Active Record and code generation. Using empty models which generated all of their fields from the database was not really a clean solution for me.
I had also a more closed mindset, but after trying out many new programming languages and having seen the presentation Java viene da Marte, Ruby da Venere (Java comes from Mars, Ruby from Venus) by Paolo Perrotta I'm ready to try again.
Let's see how Rails 3 fares: I won't be using much of the features introduced in the 3.x version as I'm starting from basic problems. The comparison I have in mind is with PHP frameworks, not with PHP as a bare language.
Rails can be installed via gem: it's the equivalent of PEAR, and require system-wide permissions. But it's only one of the options, as Rails can be downloaded as a stand-alone package.
The rails command line utility allows to generate a new project, a feature which was copied in the zf cli tool for Zend Framework in version 1.8. It also features a small built-in web server to run the project in development: this server is present in many frameworks (e.g. in the case of Java, Jetty can be embedded in frameworks) and has been copied in PHP 5.4.
A note for Ubuntu users: the last version of the distro has some issues running Rails that can be fixed. This is a fault of Ubuntu, not of Rails.
"Rails is opinionated software."
Rails professes some basic design principles:
- Don't Repeat Yourself: good for me. That's normal and expected from a solution oriented to developers.
- Convention over Configuration: it is sometimes confusing, but it's better than giant XML files for mapping fields to columns and routes to actions.
- REST and standard HTTP verbs implemented over many resources: this choice results in a standard list of actions for controllers, which in a way helps limiting how much code can be put in them.
The Hello World
Generation is at the center of Rails approach to the Hello World problem: rails new helloworld will create a new project in the helloworld/ folder. 1186 lines of code will be generated (300 are actually the README file.) It's a bit much, but it's like this for every framework nowadays.
Automation is employed not only with the rails utility, but also with rake: it's a tool equivalent to Make, Ant or Phing and it wraps the scripts for managing the database, running tests, generating documentation and so on.
After generating a controller and a view, the number of lines of code raises to 1228. To complete the hello world example, I edited app/views/home/index.html.erb and the routes file in config/routes.rb: it's standard MVC configuration in every language, also comprehending auto rendering of the right template by convention.
Now we're up and running! Actually not: I got a 500 Internal Server Error.
After googling for the error shown by Rails, I immediately find this help page, which suggests to delete a commented out line in application.js.
Now, how a commented out line of code in an auto generated file can disrupt a brand new application from working is a mistery to me. Always remember: frameworks (PHP or Ruby or Java ones) have much more moving parts than can break than an ordinary application.
Scaffolding on the blog example
The example from the documenation is about a simple blog engine.
rails generate scaffold Post name:string title:string content:text
will generate the model for the Post objects and the supporting code in the other layers. The total lines of code will be about 2000: borrowed design does not come for free. Rails generates even the migration files, though. But also fundamental stuff: the model, its controller, and views for each action consisting of a form and several others for presentation.
The generated controllers respect REST principles: for example they accept GET and POST to /posts, GET, PUT or DELETE to /posts/1.
Forms are rendered as templates: this choice is different from the one of PHP frameworks which treat them as objects, who can render and validate incoming requests.
Keep in mind that scaffolding is meant for quickly building up a user interface, not as a final solution. By the way, the generated views delegate to lots of view helpers, so most of the logic is keep out from them and from generated code (it can only be a good thing.) For example:
- generating a link to a page is always wrapped by a view helper (link_to).
- The form_for helper allows to write down a form with markup; however, the form is dynamically populated with data from a Model object, or left empty in case of a new entity.
Still, the solution is heavily oriented on Active Record. There are alternatives however: it has been some years since DataMapper could be used with Rails.
class Post include DataMapper::Resource property :name, Serial property :title, String property :content, Text end
However, DataMapper is not a real Data Mapper (yet). You will still calls post.save, mixing persistence responsibility with the business logic you can add on the model class. Maybe the Data Mapper pattern does not play well with Ruby's model of bundling up objects from many, small traits; however, as long as a model class has a dependency on the ORM it's not a pure Data Mapper pattern.
Is the generation approach capable to evolve?
Actually, the evolution is not then based on regeneration of code as the views and controllers are concise enought to be manually edited (thus generation is a one-time thing.)
As a case study, I settled out for adding a new signature field to the Post model:
- I added it to db/schema.rb and regenerated the database (can be done also with migrations of course.) I like that configuration is specified with Ruby code: the programming language is expressive but concise, allowing to insert the occasional bit of complex behavior; it saves from syntax errors and cryptic error messages derived from INI and XML files.
- I added the field to app/views/posts/_form.html.erb, as a new form element (4 lines of markup).
- I added the field to views like show, where I wanted to display it.
I will definitely delve more into Rails: it's good to consider different frameworks, and it seems to quickly be able to develop prototypes or applications tied to a database. It's also based on Ruby instead of PHP, so it allows me to learn a new language in a web-based setting.
I am also curious to tackle the real problems of web applications: editing multiple linked entities, authentication and authorization, i18n, validation and filtering of input, testing...