Models – Rails and ASP.NET MVC, Properties and Constraints


Some rather vague thoughts on models in Rails and ASP.NET MVC. Mostly out of interest in the different approaches rather than as a critique of either framework, because, well because that’s a different blog post….Let’s imagine, for this little ditty, that we are dealing with a blogging engine.

Properties

In Rails, properties don’t have to be explicitly declared in the models, so you get something like this:

class Post < ActiveRecord::Base
end

It's nice and clean, nice and simple, but, looking at the model, you have no idea what's in there. Instead, if you're a new developer on the project, you can scan through the schema.rb or dive straight into the database to find out.

Contrast that with ASP.NET MVC which takes a more explicit approach:

namespace YetAnotherBlogEngine.Models
{
    public class Post
    {
      public int Id (get; set;}
      public string Title {get; set;}
      public string Body {get; set;}
      public int Category_Id {get; set;}
      public datetime Date_Published {get; set;}
    }
}

So a new developer bounced in to the project knows what properties this model has by looking at the model rather than a schema.

I wonder, to myself mainly, if the differences are a function of the duck typed vs strongly typed nature of ruby vs c#, and the fact that with ASP.NET MVC the toolest (Visual Studio with its autocomplete and continual compilation process) was there before the framework, whereas Rails was developed as a framework before the toolset (Texmate, Vim + plugins, Host of other 3rd Party dev environments and obviously Sublime Text).

Constraints

Let's continue and add some sort of constraint - perhaps a blog post must have a title. That makes sense. I'm looking at the code first method in ASP.NET MVC because I think it provides a little more of a like-with-like comparison to Rails.

Rails takes the approach that model level validations are the way to go:

Model-level validations are the best way to ensure that only valid data is saved into your database. They are database agnostic, cannot be bypassed by end users, and are convenient to test and maintain

So, we can enhance our model like so:

class Post < ActiveRecord::Base
  validates :title, :presence => true
end

This, by itself, will not persist your constraint to the database schema. You would need to do that in your migration in Rails.

The Active Record way claims that intelligence belongs in your models, not in the database. As such, features such as triggers or foreign key constraints, which push some of that intelligence back into the database, are not heavily used.

and

Although Active Record does not provide any tools for working directly with such features, the execute method can be used to execute arbitrary SQL.

ASP.NET MVC on the other hand, is pretty strongly tied to SQL Server, or at least, the implicit assumption is that you're going to be using SQL Server, so applying constraints to a model (called data annotations) and then running your update-database (roughly the equivalent of a Rails migration) will apply your constraint at the database level as well as the model level (now I haven't validated every data annotation, but I'm talking about things like foreign keys and non-null values, and obviously this won't work with custom annotations).

So, our ASP.NET MVC model would look something like this:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.ComponentModel.DataAnnotations;

namespace YetAnotherBlogEngine.Models
{
    public class Post
    {
      public int Id (get; set;}
      [Required]
      public string Title {get; set;}
      public string Body {get; set;}
      public int Category_Id {get; set;}
      public datetime Date_Published {get; set;}
    }
}

I've added in the using statements, because you have to explicitly pop the DataAnnotations in there.

What I like about the Rails style, is looking at your model, you get a quick sense of what validations are going on because they are all collected at the top. With ASP.NET MVC, getting an overview of the constraints on a model takes a bit longer. Especially when your model grows in size, because you have to scan through all the properties to single out which ones have data annotations. Also, annotations in ASP.NET MVC are a bit of a mixed bag, because apart from constraints, you can also specify the front end display of a field. For example:

    [Display(Name = "Post Title")]
    public string Title {get; set;}

will cause your views to render "Post Title" as the label for the Title field by default. Anyway, lets not get into that too much because the data annotations are actually part of the entity framework rather than the mvc framework per se.

A true like for like comparison between ASP.NET MVC and Rails is not really possible. ASP.NET MVC, actually wants you to develop in a Model, View, View-Model, Controller paradigm. This extra layer in .NET makes more sense when you start to work with the strongly typed views and you realise that .NET doesn't actually want your views to use your models in anything but the most basic examples. Though often it feels like you are writing code for code's sake.

I'm not sure there is any punch line to this post other than maybe accepting that each framework has a philosophy and you should "render unto Caesar" in each framework and go with the overriding ideas, idioms and philosophies.

Customising your json output in Ruby on Rails 3.2

You know how it goes; you’ve found yourself developing a little application to store and display nursery rhyme lyrics because you’re a good parent and want to get the words right for your tot when you sing to them and really, who can remember all seven verses of Twinkle Twinkle Little Star. So, you scaffold something like:

rails g scaffold NurseryRhyme name:string rhyme:text

And you’re away. Typing (or in fact cut’n’pasting from Project Gutenberg). All is bliss. You nail the improvised high note right at the end of Frère Jacques and your kid smiles from ear to ear.

So now, our story breaks down. Suffice it to say you can grab your data from your auto-generated json api. And it looks something like this:

[
{"name":"Humpty dumpty","updated_at":"2012-04-11T20:07:46Z","rhyme":"Humpty dumpty sat on a wallrnHumpty dumpty had a great fallrnAll the kings horses and all the kings menrnCouldn't put humpty together againrn","id":1,"created_at":"2012-04-11T20:07:46Z"},
{"name":"Jack and Jill","updated_at":"2012-04-11T20:08:18Z","rhyme":"Jack and Jill went up the hillrnTo fetch a pail of waterrnJack fell down and broke his crownrnand Jill came tumbling after","id":2,"created_at":"2012-04-11T20:08:18Z"},
{"name":"Baa baa black sheep","updated_at":"2012-04-11T20:09:02Z","rhyme":"Baa baa black sheeprnHave you any woolrnYes sir Yes sirrnThree bags fullrnOne for the masterrnAnd one for the damernAnd one for the little boy rnWho lives down the lane","id":3,"created_at":"2012-04-11T20:09:02Z"}
]

But you want to import it into an application, that for some reason expects the json to be somewhat differently described. You need to trash the ids and dates and change “rhyme” to “lyric”. Plus, you need each rhyme nested in a “LovelyRhyme” element. Baaa (baa black sheep).

And here’s the tldr; How to customise your json output. Start by opening up your model. You should have something like this:

class NurseryRhyme < ActiveRecord::Base
  attr_accessible :name, :rhyme
end

Add in a little code, as follows:

  def as_json(options={})
     { :LovelyRhyme => { :name => name, :lyrics => rhyme } }
  end

And that will render you this:

[
{"LovelyRhyme":{"lyrics":"Humpty dumpty sat on a wallrnHumpty dumpty had a great fallrnAll the kings horses and all the kings menrnCouldn't put humpty together againrn","name":"Humpty dumpty"}},
{"LovelyRhyme":{"lyrics":"Jack and Jill went up the hillrnTo fetch a pail of waterrnJack fell down and broke his crownrnand Jill came tumbling after","name":"Jack and Jill"}},
{"LovelyRhyme":{"lyrics":"Baa baa black sheeprnHave you any woolrnYes sir Yes sirrnThree bags fullrnOne for the masterrnAnd one for the damernAnd one for the little boy rnWho lives down the lane","name":"Baa baa black sheep"}}]

Awesome. Thanks for reading.