29

It is good practice to shift logic from the controller into the model. But in any complex system, this invariably leads to a very large file even if a majority of the methods are one liners as per the Rails Way.

I've resorted to splitting up models into other modules and including them in the original model, for example, model_flags, model_validation, etc. Anyone has a better way?

Matt Hampel
  • 5,088
  • 12
  • 52
  • 78
Jaryl
  • 2,561
  • 1
  • 24
  • 33

6 Answers6

28

I realize this is a fairly old question and it's been marked as answered, but it still has good Google juice, so I figured it was worth adding to...

Rails 3 introduced ActiveSupport::Concern, which can be used to modularize behavior that's shared across models. Or, for that matter, to slim down models that have become too fat.

DHH himself provides a nice, succinct example gist here:

https://gist.github.com/1014971

tjstankus
  • 1,051
  • 11
  • 8
  • 1
    I learned about this from the Red Dot Ruby Conference (I believe from Gregg Polack), so yes I believe that going forward, this is a better way, which plays nicely with the rails core. – Jaryl Aug 09 '11 at 05:48
  • A note: the terminology (of the matter, not the answer) ended up being confusing. `ActiveSupport::Concern` is used to write models with reusable code (by many models) while the pattern `concerned_with` is a simple way to split a model into pieces. I like the first (because it instructs to use app/ instead of lib/) and I am not sure about the second (it seems convenient but conceptually dubious). – tokland Feb 03 '12 at 12:38
  • 2
    I think it's worth pointing out that concerns are just one way to address model obesity. As srboisvert notes, it's a good idea to question your object design before choosing to split a model into concerns. Here's an article that discusses that: http://blog.codeclimate.com/blog/2012/10/17/7-ways-to-decompose-fat-activerecord-models/ – tjstankus Oct 30 '12 at 20:39
6

I wouldn't do this for a few reasons.

First you violate the assumption that things will be where they should be which is probably the biggest bonus to rails in the first place. A new person can walk onto your project and navigate it quite easily if you stick model stuff in your model. If you pull it out you just add a delay and some confusion particularly if the only logic for removing something to a module is to reduce the model size.

Second you gain almost nothing from it and you lose something. File size doesn't matter these days when almost all editors and IDEs ease the navigation pain of large files. Moving stuff to a module actually takes some of this modern ease away and will require you and your colleagues or future maintainers to jump around several more files while working on one model.

That said I suspect what the hardcore rails best practice posse will tell you is that if your model is that large and complex then your design is flawed and your model is probably representing several things that could be made into separate models rather than modules.

srboisvert
  • 12,679
  • 15
  • 63
  • 87
  • 2
    Most of the behavior in Rails cannot be found 'where they should be'. Plus, by including modules into the main model file, you're already telling others where to look. I agree that navigating large files aren't that difficult, but how do you split up your models? What type of methods go first? – Jaryl Jan 01 '09 at 18:33
  • I don't split up my models. I vacillate between organizing them by functionality and alphabetically but i don't really worry that much about it. I agree with nakajima that you should pull out shared and reusable functionality where possible but more for the sake of DRY and good design than length. – srboisvert Jan 02 '09 at 15:50
  • 1
    As I note in a comment to my own answer, I think this is a valid point, particularly the last paragraph. Nothing is cut and dry when it comes to object design. Question your designs often and make your best judgement. – tjstankus Oct 30 '12 at 21:02
  • One really simple way to assess your designs (a la the last paragraph) is to read through your code, and see if there are nouns that pop up frequently. Sometimes, if you have a handful methods referring to the same noun, that signals an object/class that wants to be created. – Matt Apr 03 '18 at 14:56
3

Well, I wouldn't say that any of you are wrong to put everything in one model, but I think it is also quite valid to be able to separate various concerns. It is a trade off in the very least.

And I'm posting an answer to my own question, as I have found the Rails Way to do just that: http://github.com/jakehow/concerned_with

More information can be found here: http://m.onkey.org/2008/9/15/active-record-tips-and-tricks

Jaryl
  • 2,561
  • 1
  • 24
  • 33
2
Benjamin Crouzier
  • 40,265
  • 44
  • 171
  • 236
2

Having no knowledge of your object model, it's a bit harder to advise, but I'd say that if you're absolutely convinced that all of the validations/associations/callbacks need to be in that place, there are still ways of factoring out common behaviors. So while I wouldn't just move a big chunk of code out of one file and into another, where it just re-opens the class, I would say that using modules/plugins to describe common types of behaviors is a good idea.

For example, if you're building a Facebook-esque activity feed, and everything needs to generate "events", then maybe you'd want to move that "Eventable" behavior into a module, which when included, defines the associations/validations/etc. I'd say that approach would actually enhance the clarity of your code, since manually specifying those associations everywhere isn't nearly as expressive as declaring something as Eventable, nor is it as safe (you'd be duplicating logic in a bunch of places, and when the logic changes, you know the rest...)

All in all though, I'd say take a big look at your object model. In your test suite, if you notice that all of your tests require a lot of setup, that can be a good indicator that you're missing something in your object model. Again though, some sample code would be great.

nakajima
  • 1,862
  • 12
  • 12
0

Modules sounds sensible. I wouldn't extract the method calls (validations, callbacks, plugins etc) into modules, though, I'd limit the extraction to my own methods.

And as always, it would help if you posted some sample code. I find it hard to imagine a generic strategy to clean up models, it depends on the nature of the code.

August Lilleaas
  • 54,010
  • 13
  • 102
  • 111
  • Well, that's the thing; I don't really have a strategy. Currently, I'm simply dumping methods from file to another, which doesn't feel very rails-ish, which is why I'm here to find out what the best practice is. Cheers =) – Jaryl Jan 01 '09 at 12:03