36 comments

[ 4.8 ms ] story [ 49.4 ms ] thread
It's probably just that the example is overly simple, but...

1. The spec speed up is from good mocking, not the service object approach.

2. Tying behavior to persistence is appropriate when it's appropriate, i.e. when it happens every time the object is created/updated. He's right that a slew of after_save callbacks is obnoxious, but Rails' observers already well encapsulate such behavior.

3. In this example case, logging isn't appropriate, because he doesn't want to create an activity feed when a user is created. Instead, he only creates an activity feed entry when a user is created on the website, i.e., through the controller. In that case, it would be perfectly reasonable to include the log creation as another line in the controller code in the success branch of "if User.create(params)". For this example specifically, if that happened in many controllers it could just as easily be a method in a module that was mixed into them ("create_user_and_log"), or even a User class method ("User#create_and_log"). It's a stylistic choice rather than an architectural choice.

That's not to say the approach is without merit—it definitely has some. But what you're really doing is trading a "controller level" unit test (I've always hated that they're called "functional tests" in Rails) for a regular "class level" unit test. If that sort of encapsulation floats your boat more than, say, a module for your controllers, then by all means. I just don't see anything crazy, or heretical, or awesome here.

I use the controller mixin approach. My controller has two public methods: event and save_record. This is how they are used:

    def create
      event(:create, @literature, :notify => current_user.follows_by.users(:literature)) unless params[:skip_event]
      if save_record @literature, &set_creator
        redirect @literature , :notice => "Literature was created."
      else  
        render_form
      end
    end
Here's the code: https://gist.github.com/1120260
I haven't used rails in quite some time and have been publishing with Yii lately instead. It feels very similar. I feel like the right thing to do would be to dynamically mixin behaviors based on whether you are in the console or not. Though I guess that changes behavior between tests and production which could defeat the purpose of the tests.

Right now the big ones are that our console needs to deal with permissions and there is no user logged in via the console. Simply skipping permissions modules when in the console has been one solution.

I registered just to suggest giving XKCD credit for the comic they made (and are still hosting for you to use without bothering to rehost yourself)

"That is, you don't need my permission to post these pictures on your website (and hotlinking with <img> is fine); just include a link back to this page"

There was no reason to register here to make the suggestion, the blog has comments.
The most recent comment on that blog is 8 months old. The most recent comment by the author is 1 year old.
It's still more likely that he'll read it if you comment there, no? Or e-mail him (WHOIS has an e-mail that looks real).
Is it somehow hip to misspell "class" as "klass", or is there a deeper meaning behind it?
class is a keyword.
I think perlgeek was referring to the use of user_klass and log_klass.
Not in the way its used in his samples.
The word doesn't seem to be used by itself however - the code in the article contains "user_klass" and "log_klass", which can presumably be spelled correctly without causing an issue?
Yeah, it can, but perhaps you develop a habit for it whenever creating variable names, so there is some visual harmony between klass, old_klass, klass_dismissed, etc. when reading the code.
The k is a useful visual hint, even when not necessary.
I use `cls` in my ruby code, as I do in other langs; I think `klass` is unsightly.
... to a non-German?
... even to a German. It's Klasse, not "klass" (the ending e and the capital K are of some importance here)
Ah, correct, thanks :-)
The author of the code must like German. He used "Matz" as a parameter, a German name, and klass = class in German.
Matz is short for Yukihiro Matsumoto, the creator of Ruby.
Matz is neither a common german name (I've never encountered it) nor is class "klass" in german. It's "Klasse". Maybe you just missed out an "e" on the name aswell? There is "Matze" which would be a short version of "Mathias".
klass is used in meta programming as a way to control scope.
It is a sound engineering practice to avoid using keywords as names even when the language’s parser will let you get away with it. For consistency, some developers (myself included) will avoid words like `class` and `module` even when embedded in a longer name, such as `user_class`.

I don’t use `klass`, but that’s because I’m a Java Bigco cubicle-dweller fraudulently passing himself off as a Rubyist and Javascripter. I prefer `clazz` in keeping with Sun examples.

At first, people wanted to get away from Java with their FactoryFactoryFactoryCreationFactories. So they went to rails where everything was simpler.

Back then, every controller method had a URL.

This was fine, but had some corner cases where it wasn't. So they moved to explicit urls.

Everything was fine, but controllers got bloated. So stuff was moved into models and controllers became restful.

Now there's too much stuff in the models and we are back where we started and write blog posts about this cool new invention: The Factory.

I'm not saying this is bad. I'm not saying the people were wrong at any stage in this development. I'm just saying that as projects grow and people work to prevent old cold-smells from reappearing, to learn from earlier mistakes they slowly move to where they left off.

Until the abstractions get too complicated, the perceived bloat gets too bad. Then we'll see a new frameowrk and/or language grow, going through the same iterations.

Just wait.

I'm not complaining. I'm not flaming, I'm just thinking loudly.

This isn't exactly the factory pattern. It reminds me more of the service classes in Eric Evans' Domain Driven Design. It seems the author is addressing the problem of adding too many side effects to the models themselves.
Is it possible to design a language that circumvents these concerns?
I've been considering this approach for some of my Django projects. Right now I have an unpleasant hybrid of fat model methods and view functions that do a bit too much - having an intermediate class somewhere would clean things up a lot. My current app does a lot of denormalisation to Redis and Solr for performance reasons, which is mostly handled in the model classes - but having a bunch of non-SQL related stuff in ORM classes makes me a bit uncomfortable.
This actually represents an extremely common misconception about MVC that's common among Rails developers as well as most of the other web frameworks that have chosen some form of the ActiveRecord pattern for their ORM. Model != Persistence layer.

A Model, in the classic MVC, is a model of your problem domain with classes that represent entities and services and whatever it takes to represent your business logic in code, not just a bunch entities that get saved and retrieved from the database. Your persistence layer generally lives below the model and knows how to persist your model's entities however you so choose. The ActiveRecord pattern really muddles this distinction by putting all of your persistence code right in with your business logic. It's a great pattern when you're building something simple with minimal actual business logic, but quickly becomes unwieldily when you get into more complex situations.

My thoughts exactly... and by shoving domain logic into a bunch of EntityFooServices you might end up with code that is more procedural than OO. But maybe it's just the example that was too simplistic.
I'm a (somewhat new) Rails developer now, and I'm really confused by the premise of this article.

Why are Rails test suites so slow? For a small app, one of my test suites takes about a minute for 100 tests. That's crazy!

This author seems to suggest persistence is the issue, but that doesn't make any sense to me. A disk hit is like, what, 10ms? Even if every one of my tests hit the DB once, and if there was no caching, that still would only be 1s. If I set the development database to be an in-memory SQLite, would that magically speed up my tests 100%? (I kind of doubt it.)

When I run rake tests, there seems to be some super long delay on the rake part, and not on the test part. Is the slow test speed related to loading and unloading the code in development (which apparently 1.9.2 is slow at)? Is this due to some rake bug? How would I even find out?

The initial high load time is due to the combination of 1.9.2 and the new structure inside Rails 3. You can improve this by applying one of the approaches listed here: http://www.rubyinside.com/get-edge-ruby-faster-loading-ruby-...

or checking out the 1.9.3 release (I think it has one of these approaches applied).

That's clearly part of the answer: running 1.9.2-p180-patched instead of 1.9.2-p180 drops the total test suite time from 65s down to 52s.

But the test suite still takes 52s, of which only 12s is actually attributable to tests (since rails actually outputs the times for the test portion). FWIW, all of that measured test time seems to be for bcrypt (even with the test-specific initializer to make it faster) rather than persistence anyway.

Thanks for reducing things by 13s, at least. ;-)

EDIT: Actually, it looks like I'm down to 30--40s now, so that helped a lot. Basically, all of the non-test-time seems to be rails environment time (e.g., time bundle exec rake environment) which takes about 6-7s per run (times 3).

Yea, that's pretty much it. I would recommend looking at Spork - it pre-loads the Rails environments (the parts you want it to, at least) onto standing processes that you use to run tests. I consider Rails testing to be pretty much unusable without it.
Ah, down to 14s for 100 tests. I had originally avoided spork because it looked rspec only, but I guess there's spork-testunit as well.

Thanks!

You can accomplish the same thing without resorting to the factory pattern by simply injecting the logging behavior only in your production environment. This strategy provides the same benefit without incurring the overhead of deviating from the "Rails Way" for your development team.

Here is an example of how to do this: https://gist.github.com/1122080