# Intermediate Rails: Understanding Models, Views and Controllers

Get the Math, Better Explained eBook and turn Huh? to Aha!

I’m glad people liked the introduction to Rails; now you scallawags get to avoid my headaches with the model-view-controller (MVC) pattern. This isn’t quite an intro to MVC, it’s a list of gotchas as you plod through MVC the first few times.

Here’s the big picture as I understand it:

• The browser makes a request, such as http://mysite.com/video/show/15

• The web server (mongrel, WEBrick, etc.) receives the request. It uses routes to find out which controller to use: the default route pattern is “/controller/action/id” as defined in config/routes.rb. In our case, it’s the “video” controller, method “show”, id “15″. The web server then uses the dispatcher to create a new controller, call the action and pass the parameters.

• Controllers do the work of parsing user requests, data submissions, cookies, sessions and the “browser stuff”. They’re the pointy-haired manager that orders employees around. The best controller is Dilbert-esque: It gives orders without knowing (or caring) how it gets done. In our case, the show method in the video controller knows it needs to lookup a video. It asks the model to get video 15, and will eventually display it to the user.

• Models are Ruby classes. They talk to the database, store and validate data, perform the business logic and otherwise do the heavy lifting. They’re the chubby guy in the back room crunching the numbers. In this case, the model retrieves video 15 from the database.

• Views are what the user sees: HTML, CSS, XML, Javascript, JSON. They’re the sales rep putting up flyers and collecting surveys, at the manager’s direction. Views are merely puppets reading what the controller gives them. They don’t know what happens in the back room. In our example, the controller gives video 15 to the “show” view. The show view generates the HTML: divs, tables, text, descriptions, footers, etc.

• The controller returns the response body (HTML, XML, etc.) & metadata (caching headers, redirects) to the server. The server combines the raw data into a proper HTTP response and sends it to the user.

It’s more fun to imagine a story with “fat model, skinny controller” instead of a sterile “3-tiered architecture”. Models do the grunt work, views are the happy face, and controllers are the masterminds behind it all.

Many MVC discussions ignore the role of the web server. However, it’s important to mention how the controller magically gets created and passed user information. The web server is the invisible gateway, shuttling data back and forth: users never interact with the controller directly.

## SuperModels

Models are fat in Railsville: they do the heavy lifting so the controller stays lean, mean, and ignorant of the details. Here’s a few model tips:

Using ActiveRecord

class User < ActiveRecord::Base
end


The code < ActiveRecord::Base means your lowly User model inherits from class ActiveRecord::Base, and gets Rails magic to query and save to a database.

Ruby can also handle “undefined” methods with ease. ActiveRecord allows methods like “find_by_login”, which don’t actually exist. When you call “find_by_login”, Rails handles the “undefined method” call and searches for the “login” field. Assuming the field is in your database, the model will do a query based on the “login” field. There’s no configuration glue required.

Defining Class and Instance Methods

 def self.foo
"Class method"    # User.foo
end

def bar
"instance method"  # user.bar
end


Class and instance methods can cause confusion.

user (lowercase u) is an object, and you call instance methods like user.save.

User (capital U) is a class method – you don’t need an object to call it (like User.find). ActiveRecord adds both instance and class methods to your model.

As a tip, define class methods like User.find_latest rather than explicitly passing search conditions to User.find (thin controllers are better).

Using Attributes

Regular Ruby objects can define attributes like this:

  # attribute in regular Ruby
attr_accessor :name        # like @name
def name=(val)             # custom setter method
@name = val.capitalize   # clean it up before saving
end

def name                   # custom getter
"Dearest " + @name        # make it nice
end


Here’s the deal:

• attr_accessor :name creates get and set methods (name= and name) on your model. It’s like having a public instance variable @name.
• Define method name=(val) to change how @name is saved (such as validating input).
• Define method name to control how the variable is output (such as changing formatting).

In Rails, attributes can be confusing because of the database magic. Here’s the deal:

• ActiveRecord grabs the database fields and throws them in an attributes array. It makes default getters and setters, but you need to call user.save to save them.
• If you want to override the default getter and setter, use this:

# ActiveRecord: override how we access field
def length=(minutes)
self[:length] = minutes * 60
end

def length
self[:length] / 60
end


ActiveRecord defines a “[]” method to access the raw attributes (wraps the write_attribute and read_attribute). This is how you change the raw data. You can’t redefine length using

def length          # this is bad
length / 60
end


because it’s an infinite loop (and that’s no fun). So self[] it is. This was a particularly frustrating Rails headache of mine – when in doubt, use self[:field].

Never forget you’re using a database

Rails is clean. So clean, you forget you’re using a database. Don’t.

Save your models. If you make a change, save it. It’s very easy to forget this critical step. You can also use update_attributes(params) and pass a hash of key -> value pairs.

Reload your models after changes. Suppose a user has_many videos. You create a new video, point it at the right user, and call user.videos to get a list. Will it work?

Probably not. If you already queried for videos, user.videos may have stale data. You need to call user.reload to get a fresh query. Be careful — the model in memory acts like a cache that can get stale.

Making New Models

There’s two ways to create new objects:

joe = User.new( :name => "Sad Joe" )        # not saved
bob = User.create ( :name => "Happy Bob" )  # saved

• User.new makes a new object, setting attributes with a hash. newdoes not save to the database: you must call user.save explicitly. Method save can fail if the model is not valid.
• User.create makes a new model and saves it to the database. Validation can fail; user.errors is a hash of the fields with errors and the detailed message.

Notice how the hash is passed. With Ruby’s brace magic, {} is not explicitly needed so

user = User.new( :name => "kalid", :site => "instacalc.com" )


becomes

User.new( {:name => "kalid", :site => "instacalc.com"} )


The arrow (=>) implies that a hash is being passed.

Using Associations

Quick quiz, hotshot: suppose users have a “status”: active, inactive, pensive, etc. What’s the right association?

class User < ActiveRecord::Base
belongs_to :status  # this?
has_one :status     # or this?
end


Hrm. Most likely, you want belongs_to :status. Yeah, it sounds weird. Don’t think about the phrase “has_one” and “belongs_to”, consider the meaning:

• belongs_to: links_to another table. Each user references (links to) a status.
• has_one: linked_from another table. A status is linked_from a user. In fact, statuses don’t even know about users – there’s no mention of a “user” in the statuses table at all. Inside class Status we’d write has_many :users (has_one and has_many are the same thing – has_one only returns 1 object that links_to this one).

A mnemonic:

Well, they sort of rhyme. Work with me here, I’m trying to help.

These associations actually define methods used to lookup items of the other class. For example, “user belongs_to status” means that user.status queries the Status for the proper status_id. Also, “status has_many :users” means that status.users queries the user table for everyone with the current status_id. ActiveRecord handles the magic once we declare the relationship.

Using Custom Associations

Suppose I need two statuses, primary and secondary? Use this:

belongs_to :primary_status, :model => 'Status', :foreign_key => 'primary_status_id'
belongs_to :secondary_status, :model => 'Status', :foreign_key => 'secondary_status_id'


You define a new field, and explicitly reference the model and foreign key to use for lookups. For example, user.primary_status returns a Status object with the id of “primary_status_id”. Very nice.

## Quick Controllers

This section is short, because controllers shouldn’t do much besides boss the model and view around. They typically:

• Handle things like sessions, logins/authorization, filters, redirection, and errors.
• Have default methods (added by ActionController). Visiting http://localhost:3000/user/show will attempt to call the “show” action if there is one, or automatically render show.rhtml if the action is not defined.
• Pass instance variables like @user get passed to the view. Local variables (those without @) don’t get passed.
• Are hard to debug. Use render :text => "Error found" and return to do printf-style debugging in your page. This is another good reason to put code in models, which are easy to debug from the console.
• Use sessions to store data between requests: session[:variable] = “data”.

I’ll say it again because it’s burned me before: use @foo (not “foo”) to pass data to the view.

## Using Views

Views are straightforward. The basics:

• Controller actions use views with the same name (method show loads show.rhtml by default)
• Controller instance variables (@foo) are available in all views and partials (wow!)

Run code in a view using ERB:

• <% ... %>: Run the code, but don’t print anything. Used for if/then/else/end and array.each loops. You can comment out sections of HTML using <% if false %> Hi there <% end %>. You get a free blank line, since you probably have a newline after the closing %>.

• <%- ... %>: Run the code, and don’t print the trailing newline. Use this when generating XML or JSON when breaking up .rhtml code blocks for your readability, but don’t want newlines in the output.

• <%= ... %>: Run the code and print the return value, for example: <%= @foo %> (You did remember the @ sign for controller variables passed to the view, right?). Don’t put if statements inside the <%=, you’ll get an error.

• <%= h ... %>: Print the code and html escape the output: > becomes >. h() is actually a Ruby function, but called without parens, as Rubyists are apt to do.

It’s a bit confusing when you start out — run some experiments in a dummy view page.

## Take a breather

The MVC pattern is a lot to digest in one sitting. As you become familiar with it, any Rails program becomes easy to dissect: it’s clear how the pieces fit together. MVC keeps your code nice and modular, great for debugging and maintenance.

In future articles I’ll discuss the inner details of MVC and how Rails forms work (another headache of mine). If you want a jumpstart on the nitty gritty, browse the Rails source and try to follow the path of a request:

• WEBrick server modified to call the Rails routing library and dispatcher.
• Rails Dispatcher actually creates the controller and passes it data.
• ActionController Base defines many functions, including those to call a controller action (using appropriate defaults), render text, and return a response.

But all in good time my friends — I’ll explain it as I understand it. And if you had any forehead-slapping moments with MVC, drop me a note below.

## Other Posts In This Series

Better Explained is dedicated to helping you grasp concepts, and serves over 250k readers each month.

2. Thanks for the kind words, I’m happy you enjoyed it!

3. Very informative and helpful. Thank you.

4. Thanks Nick

5. Shin Guey says:

Great post on rail. Look forward for your next article. and thanks for the hard work on writing this article.

6. Thanks again Kalid…I am working with Code Igniter (a very nice PHP framework) at the moment and much of what you mention here is relevant as CI is similar to Rails framework.
Your articles are always informative but very easy to follow…keep on blogging

C

7. Wow, thanks for the kind words — hearing that it helps people helps motivate me to keep blogging

8. Jailyn says:

9. Anonymous says:

An off-topic question – what tool did you use to make the diagram in the article?

10. Hi, I made the diagrams using PowerPoint 2007. I created a bunch of boxes and arrows and styled/colored them appropriately.

11. Sunil says:

These are real ‘gems’ I say !! Thanks for the article..

12. Wow! This was extremely helpful, especially regarding the confusing inner workings of models. Thank you so much!

13. Kalid says:

@Sunil & MSM: Thanks, glad you found it useful!

14. Great tutorial! And thank you for posting “burned me before” tips, i’m sure these would save a lot of my “beginner’s” time. Thanks!

15. Kalid says:

Thanks! Yeah, I find many tutorials repeat the same info — it’s more interesting to say what they left out!

16. Matt says:

Yeah this article was awesome. Really got into the intermediate level stuff I needed after a few basic beginner’s run-throughs. Thanks!

17. Kalid says:

18. MB says:

Hi,

Thanks i was wondering if you can make this available as a pdf, btw i can’t seem to open your ‘Exponential-growth.pptx’ file with openoffice.

Keep up the good work

19. EW says:

Controllers nor Models are hard to debug. Just install the ruby-debug gem then place debugger within the block of code you wish to debug and run ./script/server –debugger

20. @MB: I don’t have a specially-formatted pdf version, but if you’d like something easier to view, here is the printable version (you can add /print to any article to get the print-formatted one).

Unfortunately .pptx is in Powerpoint 2007 format, so you may need that program. It is a zip file so if you rename to .zip & extract you may be able to get the files that way.

@EW: Thanks, appreciate the tip!

21. euro says:

A clear, concise and excellent tutorial.
I liked your calculus one too – more your enlightened teaching mindset!
Keep blogging.

22. Kalid says:

Thanks euro, glad you enjoyed it! I’ll keep writing :).

23. Nik says:

Dude, where’s the donate button?

24. Larry says:

uh! – what a contribution. Articles like this make the world more productive. Thanks for taking the time.

25. @Nik: No donate button yet, but I’m working on an ebook :).

@Larry: Thanks Larry, you’re welcome!

26. mike alexander naah says:

I new to rails. Try to figure out how away for my application that allow users to upload their photos. The application is store photos in the database?

27. A small clarification: actually the guys designing Rails made a mistake. The design pattern in the diagram is not MVC, but MVP (Model-View-Presenter), a derivative of MVC. In real MVC, views are supposed to communicate directly with models, and the controller only selects what model and what view to use. This does not happen neither in RoR nor in 99% of web frameworks that cloned the same mistake. And most of all, templates != views; database != models. They should lie under them.

28. Thank you so very very much for this article. I’ve been beating my head against a wall for the last three months trying to find info that reads more clearly than gibberease (meaning that it’s supposed to be educational…as long as you’ve been doing web design since the beginning and are completely familiar…no, intimate with all the principles that lead to RoR) and this is one certain gem amongst the rubble. I’ve only gotten thru ~1/4 of the doc so far, but I can understand the way you think.

Here I’m trying to learn the Rails way, but I also have daily work I have to get done to keep our fledgling site running and resolving outstanding issues the original dev left undone…I just wish I had found this months ago. Almost as much as I wish I had gotten a ream of paper for my printer today while I was out so I could print a copy right now… so, thank you thank you thank you. This is going to help so much!

29. Kalid says:

@Andrew: Awesome, really glad it helped! Exactly, so many articles make gigantic assumptions about what you already know, it can be hard to see it from the beginning. I’m thrilled this was able to help :).

30. fearless says:

@kalid: in a belongs_to association, what’s the difference between :model and :class_name? In you example, you have

belongs_to :primary_status, :model => 'Status', :foreign_key => 'primary_status_id'

but I can’t find :model documented (I’m probably not looking in the right place). I’ve used :class_name when I need two references to the same table through a foreign key. Is that the same?

(Great article, btw…)

- ff

31. Kalid says:

@fearless: Whoops, looking at it again, yes, it should be :class_name… :model may have been for a much older version.

32. Simon says:

Be careful with the method override:

def length=(minutes)
self[:length] = minutes * 60
end

This will multiply a string by 60 when used in a form, not an integer. Better to cast this:

Be careful with the method override:

def length=(minutes)
self[:length] = minutes.to_i * 60
end

33. Kalid says:

@Simon: Thanks for the tip, good catch.

34. Anonymous says:

Great Article. I just started with RoR
Thanks

35. Thanks, I’m still can’t get is a graphics render a view or a model?

36. Pete Dunham says:

You definitely know how to teach “something”.

You can make any concept into pieces and can teach that really well.

Thanks for this great article.

37. Kalid says:

@Pete: Thanks!

38. Tommy says:

39. Kalid says:

@Tommy: Awesome, thanks!

40. SJD says:

I have been dying to get an article like this

41. kalid says:

42. uday says:

Hi, I have a counter model with attributes number. I want a button to updated the number with every time its been clicked. Can some one tell how I should approach this problem? Thanks in advance.

43. Thanks so much for this article. Your write ups have been invaluable to me as I learn ruby and rails for the first time. I’m blogging about my learning process and will be sure to direct some of my readers to your content. Thanks and please keep it up!

44. kalid says:

45. Dhanu says:

Great article. Thanks, helped me a lot.

46. kalid says:

47. “There’s no mention of a “user” in the statuses table at all. Inside class Status we’d write has_many :users “. If you use has_many :users, can you query to see how many users have a specific status? For example, if we wanted to show “active” users. Just not seeing how this would work if there is no column in the status table for users associated with each status.

48. Reinier says:

Thank you for taking your time to decrypt and consolidate such a great guide!

49. kalid says:

@Reinier: You’re welcome!

50. I absolutely love your description of these different parts as people. Really makes it stick in your mind. I wish I could have read this on my first attempt to grasp MVC.

Thanks!

51. kalid says:

Thanks Cory, glad you enjoyed it!

52. Butifanu says:

You’re such a wonderful teacher Kalid. Thanks for all the valuable articles you provided!

53. kalid says:

Thanks Butifanu!

54. Good article but somehow not correct. Rails pretends to be MVC but is only MVP. I would correct that in the article. Google for it.

55. Bojan says:

Hi , this is great article .
Just keep up with good work .

Is there any other source for learn Rails in deep ?Or if you can write something about Rake, advance Routing or similar , it would be great ?

Thank you