Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

Maybe it's because I've been reading about DCI lately, but I think this example could go even further in moving business logic out of the AR model.

    class GuestbookLibrarian
        def initialize(rate_limiter, tweeter, spam_checker)
            @rate_limiter = rate_limiter
            @tweeter = tweeter
            @spam_checker = spam_checker
        end

        def add_entry(name, msg, ip_addr)
           raise PostingTooSpammy if @spam_checker.is_spammy? msg
           raise PostingTooFast if @rate_limiter.exceeded? ip_addr 

           entry = GuestbookEntry.create(:name => name, 
                                         :msg => msg, 
                                         :ip_addr => ip_addr)
           @rate_limiter.record(ip_addr)
           @tweeter.tweet_new_guestbook_post(name, msg)

           entry 
        end
    end

    class GuestbookController < ApplicationController
        ... SNIP ...
        
        rescue_from PostingTooSpammy, :with => :some_spam_handler
        rescue_from PostingTooFast, :with => :some_other_spam_handler
    
        def create
          #maybe these should be globals in an initializer somewhere if we
          #use them elsewhere? or in a :before_filter at least :)
          rate_limiter = UserContentRateLimiter.new
          tweeter = Tweeter.new
          spam_checker = UserContentSpamChecker.new

          librarian = GuestbookLibrarian.new(rate_limiter, tweeter, spam_checker) 
          entry = librarian.add_entry(params[:name], params[:msg], params[:ip_addr])
      
          redirect_to entry, :notice => "Thanks for posting"
        end
    end
Something like that? Too Java-y? Feedback appreciated :)


Interesting approach. It'd be a bit surprising in a Rails app, however, since models usually handle their own validation.

That said, the fact that GuestBookEntry doesn't have validation as a concern does make it simpler.

I might refactor to something like this if a particular model had extremely complicated validation logic, but wouldn't ever do this as a first pass.


I think in general models can handle their own validation (especially if you use AR), but I guess the difference is that these validations hit external "services", instead of like a :max_length validation.

I agree on the first pass comment - I imagine that the user stories went something like:

    * v1 - An anon user can post to the guestbook
    * v2 - Guestbook comments are checked for spam
    * v3 - Post guestbook comments to twitter for Web 2.0-ness
And this is the refactored version after v3


Not too Java-y, but too "DataMapper-y" for most Rails devs, I think (the pattern, not the ORM - the Ruby DataMapper ORM implements the ActiveRecord pattern, though DataMapper 2 actually finally will implement DataMapper)

EDIT: This is an interesting overview of where DataMapper 2 is going as a parallel to your example: http://solnic.eu/2012/01/10/ruby-datamapper-status.html




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: