4

I'm working on a rails application, and I've been pulling functionality out of my rails code and into pure ruby classes in lib/. I've found myself often writing classes like this:

 class MailchimpIntegration 
 def subscribe_email(email, fname, lname)
 Gibbon.list_subscribe(:id => NEWSLETTER_LIST_ID, :email_address => email,
 :merge_vars => {'fname' => fname, 'lname' => lname },
 :email_type => "html", :double_optin => false, :send_welcome => false)
 end
 def unsubscribe_email(email)
 Gibbon.list_unsubscribe(:id => NEWSLETTER_LIST_ID, :email_address => email)
 end
 def change_details(old_email, email, fname, lname)
 Gibbon.list_update_member(:id => NEWSLETTER_LIST_ID, :email_address => old_email,
 :merge_vars => {'email' => email, 'fname' => fname, 'lname' => lname })
 end
 def get_email_info(email)
 Gibbon.list_member_info(:id => NEWSLETTER_LIST_ID, :email_address => [email])["data"].first
 end
end

My question is: Should I change these methods to be class methods?

It seems reasonable to do, as I'll probably end up calling these by just newing up a MailchimpIntegration class each time. However, I generally prefer to have instance methods as they can be more easily stubbed etc, although this seems to be less of an issue in ruby.

I have several classes like this in my system, so I'd be keen to see what people think about this.

asked Aug 26, 2012 at 9:21
1

2 Answers 2

3

Another approach you can take is to use mixins.

 module InteractsWithMailChimp
 def subscribe_email(email, fname, lname)
 Gibbon.list_subscribe(:id => NEWSLETTER_LIST_ID, :email_address => email, 
 :merge_vars => {'fname' => fname, 'lname' => lname }, 
 :email_type => "html", :double_optin => false, :send_welcome => false)
 end
 end
 Class User
 include InteractsWithMailChimp
 end

This approach would give you the benefit of having/using instance variables from the context of the user within the module. The module can be tested in isolation by extending it within any test case. Within the module you can separate the responsibilities for example you can consider that the subscribe/unsubcribe part is a responsibility on its own so you would have:

 module InteractsWithMailChimp
 class HandlesSubscriptions
 def subscribe_email(email, fname, lname)
 Gibbon.list_subscribe(:id => NEWSLETTER_LIST_ID, :email_address => email, 
 :merge_vars => {'fname' => fname, 'lname' => lname }, 
 :email_type => "html", :double_optin => false, :send_welcome => false)
 end
 end
 end
 Class User
 include InteractsWithMailChimp::HandlesSubscriptions
 end

Within classes that are part of the module if you consider that you would not need any external data there is no problem of making the methods class ones. I like the mixin approach more because it's simple to reuse, it does not need to repeat yourself with the object instantiation and I can name my modules and classes within modules referring exactly to the responsibility they have. I would still keep these module separate in /lib of course. For more really nice tips and tricks on how to organize your rails code and have your test run fast you can check out fast rails tests. Hope it helps.

answered Aug 26, 2012 at 13:44
0

I'm putting this in as an answer, since I could be wrong, but I think I have a good reason for going with instance methods as opposed to class methods in this case.

The mailchimp class above is called from the User class when the user is saved, email edited, etc, realized that I need to make sure I don't call the mailchimp API in test, or all my tests will be super slow. (I stub out the mailchimp stuff in the tests dealing with that, but I don't for the other tests which just deal with user).

With class methods, this seems like it would be a pain since I can't easily stub out all the methods, but with MailchimpIntegration objects, I can just put:

 def mailchimp
 if Rails.env.production?
 MailchimpIntegration.new
 else
 NullObject.new
 end
 end

In my user class, and stub that out to return my stub object with I'm testing the integration.

answered Aug 26, 2012 at 10:55

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.