Nerd Dinner on Rails (Cont.)
Adding Dinners and RSVPs to Nerd Dinner on Rails
Like the ASP.NET MVC version, the Nerd Dinner application doesn’t need a complicated database. It has only two entities:
The relationships are fairly straightforward as well:
- A Dinner has 0:N RSVPs
- An RSVP has 1 Dinner
Earlier you installed SQLite 3 and the Ruby SQLite 3 gem. In order to move the process along I will use scaffolding to create the Dinner and RSVP objects (models, views, controllers, routing, etc.). Scaffolding is a way to automatically generate models, views and controllers based off the data structures. In addition, Rails creates the files that will hold unit tests. The resulting components are just a starting point. Without exception, these files will always need to be modified.
Scaffolding also creates the database migrations scripts - which is itself worthy of its own article. I won’t go into the specifics here, but it is worth your time to check out how Rails provides database versioning.
The following code scaffolds the Dinner objects:ruby script/generate scaffold Dinner
id:int Title:string Eventdate:datetime
The following code scaffolds the RSVP objects:ruby script/generate scaffold Rsvp
id:int Attendeename:string Dinner:references
Figure 9 illustrates the results of these two commands. Note that for the RSVP scaffold, the directive Dinner:references is present. This directive tells Rails that it has a foreign key relationship with Dinner. Also, notice that the primary key for each table is called id. This gets to what is perhaps the most important concept in Rails: Convention over Configuration.
Figure 9: Scaffolding in Rails creates models, views, controllers, routes, tests and other application files.
If you look in the apps folder, under the models, views and controllers folders, you will see a number of files to support Dinners and RSVPs. Take a few moments to look through the files. If you noticed, relative to models in ASP.NET MVC, there is almost no default code in a Rails Model. In Rails, the framework reads the database for its entity definition. Notice that the models are based on ActiveRecord.
ActiveRecord is actually a design pattern outlined in Martin Fowler’s Book Patterns of Enterprise Application Architecture. In a nutshell, ActiveRecord is Rails’ take on Object Relational Mapping (ORM). If you are like me and you dove head first into NHibernate, LINQ to SQL, etc., this will be a big change for you. In the Rails Framework, ORM and data persistence is just there. It is simply a service provided by the Rails framework, nothing more and nothing less. Recently, Rob Connery, who is a former member of the ASP.NET MVC Team, wrote a very thoughtful blog post on ORMs and data persistence. You can find Rob’s post here: http://blog.wekeroad.com/2009/12/26/thoughts-on-ef-vs-nhibernate-redux. Rob also wrote SubSonic (http://subsonicproject.com/), which is a .NET implementation of the ActiveRecord pattern.
Beginning with Rails 3, with the incorporation of MERB (http://rubyonrails.org/merb), instead of only having ActiveRecord, you can choose an ORM. MERB is an ORM-agnostic MVC framework. With its inclusion in Rails 3, you can continue to use ActiveRecord, or, you can choose other ORMs including Sequel and DataMapper.
With the models, views and controllers in place, are you ready to start creating Dinners? Try and navigate to http://localhost:3000/Dinners. As Figure 10 illustrates, you aren’t quite there yet. If you look in the db\migrate folder, you will find a clue to a very important step that you haven’t completed yet. You need to migrate your database changes. In this case, the migration involves creating your database entities.
Figure 10: Event though the models, views and controllers have been created via scaffolding, the database has not been updated yet.
To migrate the database changes, you’ll use another tool that is written in Ruby: Rake. Rake is a build tool that allows you to run tasks. In this case, you have two database migration tasks that you need to run. To facilitate this migration, you need to run the following command from the prompt:rake db:migrate
This command tells Rake to run the scripts in the db\migrate folder. There is actually quite a bit going on here, especially with regard to how database versions are managed, but those details are beyond the scope of this article. Hopefully, you can already see there is quite a bit of power and flexibility at your disposal. Figure 11 illustrates the results of the db migration.
Figure 11: The rake db:migrate command takes care of migrating database modifications.
OK then, with the database in place, are we good to go yet? Go ahead and try to navigate to http://localhost:3000/Dinners. No error this time! As Figure 12 shows, it’s not pretty, but it is almost 100% functional. Go ahead and click the new link. Figure 13 illustrates the error.
Figure 12: The index page for Dinners, which at this point, is just a raw list.
Figure 13: This error results from the fact that the Dinners model is not aware of its relationship with the RSVP model.
If you recall, when scaffolding the RSVP entity, you indicated that an RSVP referenced a Dinner. In other words, the RSVP is the many side of a one-to-many relationship with Dinners. The following is the code for the RSVP model:class Rsvp < ActiveRecord::Base
As you can see, an RSVP belongs to a Dinner. All you need to do is modify the Dinner Model so that it knows that it has many RSVPs:class Dinner < ActiveRecord::Base
has_many :Rsvp, :dependent => :destroy
With the added code, Rails is not only aware of the relationship when a Dinner is deleted, its related RSVP data will be deleted as well.
Figure 14 illustrates the new.html.erb view. The functionality is very raw. At this point, you can create Dinners. However, the Dinners Controller does not provide a way to associate RSVPs to Dinners. But don’t worry, you’ll get there!
Figure 14: Rails scaffolding builds out your initial views and provides functionality to create, edit and delete data.
Convention Over Configuration
A term you hear often today in development is Convention over Configuration or CoC for short. The idea is that you are faced with two choices. First, you can assume no defaults exist and you can specify, in code, what the defaults should be. Second, you can subscribe to conventions that specify defaults, thereby reducing the code you have to write. Well written applications, regardless of platform, have one thing in common - no more code than is necessary to solve a business problem. In Rails, the naming convention for the identifier is “id”. If you subscribe to this convention, Rails will take care of wiring things up for you. If you don’t subscribe to the convention, you will need to “code around” the default convention for your application to work. Conventions are repeated patterns that have the benefit of increased reliability. Reliance on conventions tends to result in more reliable, less bug-ridden code. My advice - subscribe to conventions. Value is determined by the quantity of problems solved, not the quantity of code written.