

Crazy, Heretical, and Awesome: The Way I Write Rails Apps - telemachos
http://jamesgolick.com/2010/3/14/crazy-heretical-and-awesome-the-way-i-write-rails-apps.html

======
pilif
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.

~~~
jvoorhis
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.

------
mikeocool
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.

~~~
lepacheco
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.

------
ianterrell
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.

~~~
epochwolf
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>

------
sugerman
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"

~~~
epochwolf
There was no reason to register here to make the suggestion, the blog has
comments.

~~~
sugerman
The most recent comment on that blog is 8 months old. The most recent comment
by the author is 1 year old.

~~~
JoachimSchipper
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).

------
pbh
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?

~~~
keeran
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-...](http://www.rubyinside.com/get-edge-ruby-faster-loading-
ruby-1-9-2-now-4973.html)

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

~~~
pbh
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).

~~~
swampthing
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.

~~~
pbh
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!

~~~
arikfr
Spork is awesome, but has its "gotchas" when it doesn't reload properly. I
recommend reading this:
[https://github.com/timcharper/spork/wiki/Spork.trap_method-J...](https://github.com/timcharper/spork/wiki/Spork.trap_method-
Jujutsu) and applying whatever there is relevant to your project and also
using Guard-Spork.

------
simonw
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.

------
perlgeek
Is it somehow hip to misspell "class" as "klass", or is there a deeper meaning
behind it?

~~~
DanielStraight
class is a keyword.

~~~
arctangent
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?

~~~
pak
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.

------
natehop
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>

