Concerns in ActiveRecord

Posted by Jamis on January 17, 2007 @ 02:02 PM

Ruby does not support multiple-inheritance. Personally, I have mixed feelings about that, but the fact of the matter is, you can accomplish almost exactly the same thing using modules.

Consider this ActiveRecord scenario. In Basecamp, we allow files to be attached to both messages, and comments. One way to do this would be to have both messages and comments inherit from a common ancestor, via single-table inheritance. Alas, the two models (in our case) are different enough that STI is not a viable option here.

Instead, we employ “concerns” (as David has started calling them). A “concern” is simply a module that we mix in to both the message and comment models, which sets up the common “file attachment” functionality. We’ll call this module “Folder”, to denote that any of its clients essentially become a container for files. Observe:

1
2
3
4
5
6
7
8
9
10
11
module Folder
  def self.included(base)
    base.has_many :files, :as => :owner, :dependent => :destroy
  end

  # This returns the path where files are to be uploaded
  # for this specific "folder".
  def attachment_path
    "/path/to/files/for/#{id}"
  end
end

Then, we mix the Folder concern into whichever models we want to allow files to be attached to:

1
2
3
4
5
6
7
8
9
class Message < ActiveRecord::Base
  include Folder
  #...
end

class Comment < ActiveRecord::Base
  include Folder
  #...
end

Because of the included hook, defined in the module, each class that receives this module will automatically gain a “has_many :files” association. Also, because the module is mixed into the class, you can put as much or as little extra functionality there as you want to share between the classes.

This has proven to be a very powerful pattern. To accommodate it, we’ve recently taken to creating an “app/concerns” directory in our projects, and adding that to the load path in config/environment.rb:

1
config.load_paths += %W(#{RAILS_ROOT}/app/concerns)

Our modules then go in that directory.

Posted in Tips & Tricks

Comments

Have something to add? Click here to leave a comment.

17 Jan 2007

1. Lori Olson said...

That multiple inheritance thing is crap. Ruby’s modules are, as your article points out, extremely powerful, and do a far better job of synthesizing multiple inheritance than, say, Java interfaces.

Thanks for the tip about “concerns”. It may prove useful to us.

2. Jon Leighton said...

I like that!

3. Michael Schuerig said...

Jamis, is there any reason to put concerns into /app/concerns? I think they very much belong in /app/models as they implement model functionality. If a concern is reusable, I’d say put it into /lib or even make it into a plugin. But /app/concern? No, that’s focusing on a rather uninteresting aspect of the concerns, it’s a purely technical distinction with no relevant semantics.

4. Jamis said...

Michael, we originally put them in app/models, but found it cluttered that directory and made it hard to tell at a glance which files were true models, and which were modules that extend models. We prefer the app/concerns separation.

5. Eric Anderson said...

I’m not sure I understand what the difference between a “concern” and a mixin is. Seems to be just another term for mixin. For example the Enumerable mixin applies enumerable functionality to a variety of objects. In this case the “Folder” mixin applies folder functionality to a variety of objects.

My question is why create yet another term. Seems the Rails community is overrun with terms. It’s ok to coin a new term when you have a new idea. But this is just mixins applied to models. Why not just call it that. A mixin applied to a model.

Maybe I am missing something?

6. Silvio Gissi said...

Jamis, amazing way to tackle the problem, the use of “app/concerns” was very clever as well. Using /app/models or /lib would be readable until a certain amount of classes but doesn’t scale too far ;)

7. Jamis said...

Eric, it’s not an official Railsism. It’s something David and I have taken to calling mixins that are specific to ActiveRecord objects. Call it whatever you want. My objective in this post was not to offend semantic sensibilities, but to point out a powerful way to use mixins with ActiveRecord. Yes, you are right, “concern” as used in this post refers to a “mixin.” I tried to be up-front about that at the beginning of the post, but I apologize if it wasn’t clear enough.

8. Tony Hillerson said...

Thanks for the tip Jamis.

The Concern meme is used in Aspect Oriented Programming too (as I’m sure Dave knows). Although this isn’t an aspect oriented problem or solution (strictly), the idea of a concern being a feature that disparate areas of an application share, doesn’t rely on or confuse the fact that at a lower level the feature is implemented across those different areas using a Mixin. I think the term is well applied.

My only “concern” would be if you wanted to start implementing some AOP and wanted to organize any aspects using the ‘concern’ namespace things would get confusing, but that’s getting pretty picky. Again, nice tip.

9. Tim said...

How do you avoid naming conflicts – especially between private auxiliary methods in different modules mixed into a class?

Ruby seems to overwrite those when mixing in a second “concern”. That way the code from the first module breaks, because its private auxiliary methods are replaced.

For changes to the public interface of a class this overwrite-behavior might be alright, but I always get bad feeling when declaring those private methods – especially because their names tend to be quite simple and recurrent.

10. Jamis said...

Tim, if you are defining private methods in a mixin, you should make sure they are going to be named uniquely. Prefix the method names with the name of the module, or something to that effect:

1
2
3
4
5
6
7
8
module Folder
  #...

  private
    def folder_something_special
      #...
    end
end

That’s my take on it, at any rate.

11. Chris said...

Love it! We do this too. Helps a ton.

The only difference is we have 4 – 5 subdirectories under our models/ directory. One for cache objects, one for ‘concerns,’ one for tableless models, etc. Maybe try that out if you don’t like app/concerns/

12. Tim said...

Thanks for the incredibly quick answer :)

Prefixing might be the best choice, though it’s quite verbose.

This was the first thing that really annoyed me when learning ruby. It’s the same problem with private methods that a overwritten by private methods in derived classes. It’s just counter intuitive to me…

13. Benjamin Curtis said...

I did this same thing for my e-commerce application (and documented it in my Rails e-commerce book) for the very similar Cart, Sale, and Shipment models. It worked like a charm!

14. Ron Green said...

Thanks Jamis! I didn’t know about the included hook. Great post.

15. Alan Whitaker said...

Nice approach, thanks Jamis.

Am I inferring correctly that a File model using something like belongs_to :owner, :polymorphic => true would be sitting on the “other side” of the association addressed in the concern?

When I read about extending associations in your 09-Jan post, I was thinking about some cases where I might need to declare the same association helper extensions in multiple host classes. Do you sometimes use the concern as a common place to do this part as well?

16. Jamis said...

Alan, correct, the File model has a polymorphic belongs_to that ties it to the owner.

And yes, we do the extension thing frequently in these “concerns” mixins. It’s a great way to centralize when you have multiple client classes that share identical associations.

17. Alan Whitaker said...

Makes a lot of sense. I’m excited to use it, thanks as always.

18. Tim Lucas said...

It’s interesting to watch the same abstractions emerge, though we haven’t really started moving our “concerns” into their own directory. I’m often finding that most STI designs I come up with have a much better alternative, usually involving “concerns”.

Something nobody’s mentioned is this is basically your acts_as w/o the convenience macro. acts_as macro’s are convenient when you need per-class configurable options, but the lack of documentation in creating your own acts_as, how to validate config options and storing those config options limits their accessibility.

It’s great after all this time to hear how the core team uses the framework they created, or helped to create. Thanks Jamis!

19. Michael Schuerig said...

I’m going to be the party pooper. I don’t like the idea of putting mixins into /app/concerns.

I’m very much in favor of extracting common functionality into mixins. But these mixins don’t belong into a separate folder from other model classes when their only distinction is that these mixins in contrast to those other classes are not subclasses of AR:B. The difference is purely technical, not motivated by anything in the domain.

Jamis says in #4 that they moved mixins out of /app/models in order to avoid cluttering that folder. In my opinion they’ve chosen the wrong way. Now they’re cluttering two folders instead of one. A better solution would have been to use (to be able to use, that is) a hierarchical structure of subfolders in /app/model. A structure that corresponds in some sensible way to the domain being modeled.

Regarding the terminology, I don’t like using the term “concern” for this kind of mixins, either. For one thing, there already is the perfectly adequate term “mixin”. More importantly, the concept of a concern belongs squarely in the area of analyzing and formulating problems, whereas in this case it is applied to a technical solution.

The term “concern” figures prominently in the admonition to separate concerns. It states a problem, is about what, not how. Please don’t frivolously mess up terminology.

So, why am I making such a noise instead of simply ignoring the issue? Well, Jamis and David have a lot of weight in the Rails community. Their example is bound to be copied by a lot of others—even in cases such as this where it is ill-advised, as I’ve tried to argue.

20. Jean said...

I Like the idea of extracting common functionnality of models into mixin and have been practicing this too. I haven’t found an organization that I like yet, and I am kind of skeptical of putting the into app/concerns. I think I will try and put them in app/models/mixins which seems to make more sense to me. Thank you though for reminding me that the app and app/models folder are not sacro-saint and that sub folders can be created there, for some reason, I didn’t dare lest it damages the beauty of the original layout :)

18 Jan 2007

21. Juanma said...

Jamis, I have found the idea very interesting, I was using STI for a problem like this and I was having some troubles specially making a RESTful interface for these resources. Can you give a clue for the best way to implement nested Restful routes for your sample objects Message, Comment and File. Is this possible? :)

22. Manfred Stienstra said...

I would really like to see a followup on how you test the Concerns.

23. James Adam said...

It might be worth pointing out in this specific example that you need to be careful about using the ‘id’ of the actual instance within with the module methods will operate.

In this case, the attachments for Message #123 and Comment #123 will be stored in the same place (this obviously wouldn’t be the case if using STI, since you could be guaranteed that there was no Message and Comment that shared an ID).

It’s just an aside, and doesn’t change in the slightest the usefulness of moving the functionality out into a model. It migh also be worth pointing out that this the basis of how most plugins enhance Rails’ classes. Ain’t Ruby sweet? :-)

24. Neeraj Kumar said...

As Michael pointed out why not have a subdirectory under models to have concerns. I guess that’s a personal take and not a big deal. Thanks for the tip.

25. Martin said...

I’d just like to add my support to Michael Schuerig’s comments: please don’t invent new terminology when it isn’t needed. What’s wrong just calling them ‘mixin’s and be done with it. One thing less for people to have to learn… KIS.

26. Edd Dumbill said...

Just wanted to thank you for your energy in these tips & tricks posts, it’s great to keep learning from them.

27. Matt White said...

Thanks for the great tips! Regardless of the sniping over the details of this tip, the general idea is an excellent one, and is something not usually thought of by newbies to metaprogramming like myself. Thanks!

28. DHH said...

Mixins are the implementation for concerns. But mixins is a much broader concept that’s used for many other things than just concerns. See the partitioning of big classes that we use mixins for in the Rails source for example.

Concerns, on the other hand, are just about shared business logic concepts that apply to multiple model classes. That’s a more narrow definition and one closer to the surface of the application.

Naturally, this semantic distinction may or may not be useful to you. But just as the flash could have been called the “one-request session container” or partials could have been called “subtemplates” or Ajax could have gone unnamed, I personally find it helpful to use new labels for certain uses or combinations of general concepts. That keeps us from constantly having to explain their usage and purpose.

29. Michael Schuerig said...

David, I don’t agree with your semantic distinction and your example is a great case in point. The partitioning of ActiveRecord::Base by means of mixins precisely exemplifies separation of concerns such as transaction handling and validation. By your usage of “concern” these later are not concerns as you suggest to reserve the term for “shared business logic”.

I contend that the common understanding of a concern in software development is very much broader and all the uses of mixins we’re discussing here fall under the umbrella of implementing concerns.

Also, the separation of models and concerns/mixins seems artificial. Ordinary classes, mixins, persistent classes can and should all be part of the domain model. Segregating them on the basis of technicalities such as classhood or persistence seems artificial. I’d much prefer a principle of organization based on coherence. Consider Uncle Bob’s Common Closure Principle and the Common Reuse Principle to see what I mean.

30. Chris Malek said...

I think I finally understand what a “mixin” is. Thank you for this example. I never quite understood by reading other sources.

31. Blake Watters said...

I’ve been using a similar approach to the “Concerns” concept for around a year now. My solution is an abstraction I just plainly call a “Mixin” and it lives in app/mixins. Mixins are loaded declaratively (similar to helpers, etc) into models and/or controllers. It’s an abstraction I use to keep bits of logic in a common place to build features where the model and controller interact.

For example, in Near-Time (http://www.near-time.net/) we track visits to each content node to present the user with blue stars that indicate new or updated content. There are two bits to the plumbing, a model portion for associating the Visit records with the content models and a set of declarative controller hooks for creating after filters that visit the content node. It is implemented as the ActsAsVisitableMixin, which has nested modules that mix the appropriate methods into the controller or model classes.

class Article < ActiveRecord::Base mixin :acts_as_visitable end

The model now has visits, visit!, most_recent_visit_for_user, etc. methods added.

class NewsController < ActionController::Base mixin :acts_as_visitable track_visits_to :@articles, :only => [:index, :list], :children => :comments end

The NewsController now transparently creates the Visit records for articles placed on the @articles instance variable cleanly.

I find this highly convenient for cases where the model and controller need to work in lock step to implement a feature with maximum reuse. In the mixin test, I can create a mock controller and subclass a known model to test the entire feature in one file. It may rub some the wrong way to see the controller and model logic entwined in such a way, but I think the abstraction and contained test/implementation keeps things very clean.

Anyway, just thought I’d throw my take on a similar pattern out there. The mixins implementation is available as a plugin from the Near-Time public source code repository for those who are interested: https://secure.near-time.com/svn/plugins/trunk/mixins/

Cheers, Blake

32. carlos said...

DHH, that was a pretty horrible explanation for how you have invented “concerns”. There is nothing different from a “concern” and a mixin. Ruby does not support multiple inheritance, instead they use mixins which

“Concerns, on the other hand, are just about shared business logic concepts that apply to multiple model classes. That’s a more narrow definition and one closer to the surface of the application.”

I’v read that over and over and I cannot see what the difference between that paragraph and a mixin is. Are ActiveRecord models not classes? Are “concerns” not applied to multiple classes? The only purpose of a mixin us to be used as a place holder of methods and logic to share between classes. Its really probably not a good idea to be introducing new vocabulary for terms that are already being used. It confuses the newer members of the community and in general is just not good.

But besides all that, I admit you did come up with a good folder naming structure. For my “concerns”, I put them in lib/ with the naming convention of <functionality>_shared.lib. The folders are getting rather large though, so…......

33. mk said...

“As you start to write bigger and bigger Ruby programs, you’ll naturally find yourself producing chunks of reusable code—-libraries of related routines that are generally applicable. You’ll want to break this code out into separate files so the contents can be shared among different Ruby programs.” —Module

34. Jamis said...

Alright people, time to calm down. Neither I nor DHH are suggesting that you ought to be calling these things concerns. I only said that DHH and I are. These won’t find their way into Rails core—they are “just” modules, after all. Thus, nobody needs to worry that we’ll be force feeding yet another term to the world.

However, I’ll take one last stab at defending our choice of words. Yes, it is a mixin. But so is Enumerable. So is Comparable. What we have here is something specific to ActiveRecord models, something which (very specifically) adds associations to any model that includes it. We’ve chosen to name this specific pattern a “concern”. It’s so much easier to have a short name for something, than to have to call it “a mixin for models that adds an association.” Consider “Ajax”. Isn’t that nicer than “XML HTTP Request”?

Don’t like the term yourself? Don’t use it.

End of discussion, seriously. The point of the article was to demonstrate the pattern, not to bicker over the name.