Use model callbacks or observers for implementing activity feed? - ruby-on-rails

I'm implementing an activity feed for a client similar to Twitter's (it's only activity that pertains to the current signed in user -- i.e. who favorite his/her post, mentions, etc..).. It won't rely on 'push' but instead, the user will have to refresh the page in order to see new activity (for now).
I've been googling & searching around SO for the past hour to find the best way to implement this, and observers keep coming up in the solutions. I also notice that many of these are using push notification. I noticed the approach R Bates took in his public activity railscast btw, which is why I'm asking this question.
What if I don't want to use push notification, would callbacks be ok or even better? Do you think I would still need to use implement other things outside rails for scalability? (like how you may use "Pushapp" for push notifications)
Any suggestions on better solutions or light shed would be helpful.
This is for #gg_s
I'm assuming in this case you're saying I have an activity_feed table (receiver_id, sender_id, activity_type, & activity_id) (belongs to user, belongs_to activity_type (???), :polymorphic => true)
# application_controller.rb
def publish_to_user_feed(message)
current_user.activity_feed << message
end
# favorites_controller.rb
def create
# blah blah blah
publish_to_user_feed "This just happened."
end
In the the favorites_controller's 'create' action, "This just happened" could == "#favorite.user just favorited #favorite.post by #favorite.post.user"
Again, I hope I'm not being too pesky & am pretty sure what I'm asking is obvious, but I think this will help clear things up for me & also help future visitors.
Thanks again
For anyone that wants to know, I'm still working on this.. Just took a little break.. My main concern is how heavy it'll be on the db & other performance issues so if anyone wants to better this (using the code above), feel free :)
Solution: I don't want to overcomplicate things so I'm taking ap's advice.

Use neither.
Callbacks and observers are more complex in this case than you might think. The only automation they provide is the ability to be triggered upon model events. That's it. You are responsible to implement logic determining:
what just happened?
should it be reported?
what to report?
Extending this logic to support several types of activities is needlessly complex. Ditch the automation and publish activities from the controller as they happen on an as-needed basis.
Create a helper method to keep things DRY:
# application_controller.rb
def publish_to_user_feed(message)
current_user.activity_feed << message
end
Then manually post to a user's feed when and where necessary:
# some_controller.rb
def some_action
# perform some action
publish_to_user_feed "This just happened."
end
Reporting directly from the controller is clear, readable, DRY, maintainable, and adhere's to Rails' MVC pattern. No complex callback chains or observers to write.
As a bonus, it is trivial to perform activities without posting to activity feeds, e.g. administrative activity or user privacy settings.

Related

Rails 5 - Best way to prevent emails from being sent to unsubscribed users

I am using Rails 5.
I have an Affiliate model, with a boolean attribute email_notifications_on.
I am building a quite robust email drip system for affiliates and can't figure out where the best place is to check if the affiliate has email notifications on before delivering the email.
Most of my emails are being sent from Resque BG jobs, a few others from controllers.
Here is an example of how I am checking the subscribe status from a BG job:
class NewAffiliateLinkEmailer
#queue = :email_queue
def self.perform(aff_id)
affiliate = Affiliate.find(aff_id)
if affiliate.email_notifications_on?
AffiliateMailer.send_links(affiliate).deliver_now
end
end
end
It seems like writing if affiliate.email_notifications_on? in 10+ areas is not the right way to do this, especially if I need another condition to be met in the future. Or is this fine?
I thought maybe some sort of callback in the AffiliteMailer would work, but saw many people advising against business logic in the Mailer.
Any thoughts/advice would be appreciated.
To be honest, I don't think any better way than creating a method in Affiliate model as follows,
def should_send_email?
# all business logic come here
# to start with you will just have following
# email_notifications_on?
# later you can add `&&` or any business logic for more conditions
end
You can use this method instead of the attribute. It is more re-usable and extendable. You will still have to use the method in every call. If you like single liners then you can use lambda.

Rails 4.2 Dry up Controller

I have a User model which gets accessed through a REST API and ActiveAdmin. Since there is a lot duplicated code (almost all) it might be a good idea to dry things up. There are a lot of topics out there handling this issue but i do not find a proper way. I put some of the ideas i have below
UserController instance in each controller
Again duplicated code gets created and the functions
#AA
controller do
def create
#userController = UserController.new
#userController.create(params)
end
end
#REST API
#Create instance through callback
def create
#userController.create(params)
end
Inheritance
I would like to have one basic CRUD controller lying above my model through inheritance like this
Class BaseUserController < ApplicationController
Class Api::UserController < BaseUserController
but (yet) i dont know how to do this in AA also in mind with the inherited_resource gem in mind
Concerns
Also concerns seem to be a nice way. But i would then create seperate concerns for each action to have one concern to exactly handle one behaviour
aspect and also to make them more reuseable . This then again seems to unnecessarily bloat the app and also concerns are not really made to be used like that. (?)
Modules/Mixins
Since modules cannot be instantiated but implement actions fully, this might just be what i am looking for. So each controller includes the desired actions or extend fro ma model rather than a class then.
The thing is - the more i read the more i get confused. I hope some of you guys might help me bring some light or i might turn to the dark side an use C# and .NEt again :D
Thanks in advance!
Example Code
I took this create method for a SMS resource in both, the REST API and the AA, where i call a couple of Services, but if complexity grows i have to add these lines in two places. How could i avoid that? Cant i have a basic SMSController which is used from the API endpoints as well as the AA interface?
controller do
def create
#customer =Customer.find_by_phone(params[:sms_message][:phone])
SmsService.sendSMS(#customer[:phone],Cryptoalgorithm.sms(#customer[:id], #customer[:recharge_token],params[:sms_message][:text].to_i)
#sms = SmsMessage.create(:customer_id => #customer[:id], :text => params[:sms_message][:text], :phone => params[:sms_message][:phone])
#customer.increment!(:recharge_token)
redirect_to admin_sms_message_path(#sms[:id])
end
end

Rails Thread.current[] & thread safety

So there I was, right. Just looking through some code, studying bits and pieces when all of a sudden, my ocular receptors were assaulted by the unfamiliar. I was like:
What!!! What's that all about!
Anyway, what I saw was (source):
def authenticate_user!
if doorkeeper_token
Thread.current[:current_user] = User.find(doorkeeper_token.resource_owner_id)
end
# ...
end
So after looking at it for a while, thinking:
Wtf is this Thread.current[] insanity? Is this even necessary? What's it even trying to do?
It seemed to me it was kind of like wearing a baseball cap backwards: You may look pretty f'ing cool but that sun glare is winning. I then decided to Google around, read some articles and some SO.
None seemed to concisely answer my question: Given the context of the code, would it not be the same as:
def authenticate_user!
if doorkeeper_token
#current_user = User.find(doorkeeper_token.resource_owner_id)
end
# ...
end
If not, what situation/scenario is it useful/protecting against?
I hope you enjoyed my story and want to contribute an awesome ending.
There are different bulletin boards on which you can pin information. The bulletin board where local variables are posted is hidden behind hedges and cannot be seen by the views who live in the next yard:
current_user = User.find(doorkeeper_token.resource_owner_id)
The bulletin board where #variables are posted is nailed to the top of a ladder, so the views have an unobstructed view of the #variables over the tops of the hedges:
#current_user = User.find(doorkeeper_token.resource_owner_id)
But other methods, which live in classes across the street, cannot see the #variables posted on the bulletin board on top of the ladder because a row of trees is in the way.
Thread variables, such as:
Thread.current[:current_user] = User.find(doorkeeper_token.resource_owner_id)
launch a kite, which flies higher than the trees, and the variables posted on the kite can be seen by the methods that live across the street.
Why not create a ruby global variable, e.g. $current_user, instead? Because then requests initiated simultaneously by different users will write to the same global variable, potentially screwing things up.
Given the context of the code
There's not enough context to tell why a (thread) global variable is needed.
From the author:
I use Thread to store current user to be able transparently
authenticate user and extend this later. Currently this code gives
priority to token-based authentication via Doorkeepr, and you can
extend it to any other algorithm. I am not a big fan of Devise and did
not want to use the token-based authentication strategy it provides.
Once you write user info inside Thread.currrent[:current_user], any
other service can read it independently and use for whatever purposes
it is needed. For example, as i mentioned above, checking
authorization, logging who did the events happening in the system, etc
as for writing it to #current_user, i am not sure it will be
accessible in all contexts. But i have not really checked this
Reference: https://github.com/rilian/devise-doorkeeper-cancan-api-example/issues/1#issuecomment-143479288

Determining if an association between two models has been created within one model?

I've been researching this topic for a day now, and I haven't seen a solution that could adequately allow this. I would have even give up and said that it's not possible, but I see large companies achieving this in their apps!
I need to know if the current user is following another user. I need to know this many times (for the current_user) without polling the DB again
The solution needs to be friendly for reuse. A solution (that's not friendly for reuse) I had come up with is as follows:
module UsersHelper
def is_following?(user)
return false if current_user == user
user.is_following = Relationship.find_by(followed_id: user.id, follower_id: current_user.id)
end
end
is_following?(#user) can now be called in any controller
Notice that I'm able to access current_user because this helper method will be called in a controller, not a model.
This implementation is cool for one model, maybe two... except I need to do this in almost every many-many relationship I have in the app. So it has to be scalable.
I'm referencing exactly what Twitter does with their following.

Where should I place a "notify user" method in a Rails app (or any MVC app)?

I have heard several views on MVC structure in a web application. Some people believe the model should be very small, and only contain ActiveRecord (or some other ORM) and small things like validates and belongs_to, so model's are only a couple lines long.
I personally think the model can play a larger role in apps. For example, I need to notify a user frequently throughout my app. The notifications can trigger from multiple controllers. They all send a notification to a user based on his or her notification settings, which is an object accessible through the user model.
I would like to make something like:
class User < ActiveRecord::Base
#validations, relationships, etc
def notify(event)
notif = Notification.new
notif.type = event.type
notif.to = self.id
# etc
if notif.save
# send the notification based on the settings
end
end
end
This would make it possible from any controller to use #user.notify to deliver messages to a user. This makes things really clean, but I realize there is some logic in my model. Not to mention the model makes another model.
Another approach I wouldn't mind is creating the notification and sending it through that object. So controllers could do Notification.new(:to => #user.id, ...) and then deliver the message via a Notification.send. This would also put some logic in the Notification object so it knows how to send itself. In fact, I might like this approach better than creating the notification object through the User model.
I don't mind these small bits of logic in the Model to make things convenient when sending messages from multiple controllers. Is this the best way to go about it? I suppose a more purist method is to include a NotificationHelper module in each controller that sends notifications?
edit: I've been reading a bit about "domain logic" in models. It seems like this is encouraged, and I suppose the notify or send methods are considered domain logic. Can any developer experienced in MVC comment on this?
For your second approach you can use ActiveRecord::Observer
class NotificationObserver < ActiveRecord::Observer
def after_create(notification)
# some sending logic
end
end
But don't be shy extract small pieces of logic into their own classes, like
lib/user_notify.rb
class UserNotify
def event_trigger(user,event)
# some logic
end
end
use naming concepts suitable for your system (maybe Notify.send_user_about_event, etc.)

Resources