Topic: Two Similar, yet different Tasks...how to DRY?
I have an existing application at birdsite.org. This site lets people upload bird photos. Other users can comment on the photos. When a comment is made, all users subscribed to that image will receive an email notice.
I'm now adding a forum. I want the same subscription behavior to occur when someone adds a post to a forum topic. The only differences with the image subscriptions is that the forum subscription references a Forum Post ID instead of a Image ID, and the content of the generated email refers to the forum instead of an image.
How would you keep this DRY? I'm mainly interested in making the Ruby code DRY, but it might be nice to store subscription data in one table as long as it's not too contrived.
So far all I've done is make the email template flexible enough to say image or forum, and provide the proper hyperlink. But I've got two very similar subscription models and tables, and similar code for making the notifications.
This code is representative of what I'm doing, but not the exact code:
# image_subscription table contains: id, image_id, user_id
class ImageSubscription < ActiveRecord::Base
validates_uniqueness_of :image_id, :scope => :user_id
# image_subscription table contains: id, forum_topic_id, user_id
class ForumSubscription < ActiveRecord::Base
validates_uniqueness_of :forum_topic_id, :scope => :user_id
# My ImageComment class has an after_create method that subscribes
# the current user and sends notifications
new_subscription = self.image.image_subscriptions.build()
new_subscription.user_id = session[:user_id]
subscriptions = ImageSubscription.find_all_by_image_id(self.image.id,
:conditions => ['user_id <> ?', session[:user_id]]) rescue nil
subscriptions.each do | subscription |
self.image.id, self.id, subscription.user.email)
# note error
# My ForumPost class has an after_create that looks just like the above but
# refers to the ForumSubscription model and the Mailer::deliver line replaces
# all the references to 'image' with 'forum_topic'.
Suggestions are appreciated.