Ruby on Rails: Do you recommend using Observers? - ruby-on-rails

I notice it's used primarily for sending emails. Let's say I want to send an email after every comment is created.
Is using Observers really necessary when you could just place the Mailer.deliver_email(user) in your comments_controller.rb's create action instead?

For proper programming practices, yes. The observers decouple the code and make sure it stays maintainable.

Related

Rails best practise for accessing API wrappers in application

I'm creating my first Rails application and I'll be integrating a few other apps, like Mandrill, Xero and such.
I'm just wondering what best practises are on storing instance variables globally. For example, I don't want to configure my Mandrill wrapper instance in each of my controllers. I want to do it in one spot and make a call to it whenever I need to send an email out.
Is application_helper.rb the spot to do this?
I would create a service for that. As the matter of fact you can check http://blog.codeclimate.com/blog/2012/10/17/7-ways-to-decompose-fat-activerecord-models/ which is a great way to do your applications flexible, testable and easier to maintain.

How to use EventMachine(superfeeder-ruby gem) within a Rails controller?

thank you for taking a look at this.
I am new to rails, unfortunately. I currently have to implement an endpoint that Superfeedr can push updates to, but that endpoint has to be in a rails controller.
Initially it seemed to me that this should be a background job that runs and the rest of the web tends to agree, but I am being pressured to include this as a rails controller - which confuses me. I am not certain how to include EventMachine in a request/response cycle.
I know the web is full of examples, but none really answer my question with how to route this. I have no idea.
I have a rails controller, called Superfeeds. I want Superfeeder to push updates to something like myrailsapp/superfeeds/
Inside feeds I want to inspect the pushed content, and then write the results of that to another controller that actually has a model and will persist it.
Basically, the controller called Feeds just needs to receive and pass the information along. This confuses me however because it seems to have to implement something which is a long running process inside of a rails controller - and I am not even sure if this can work.
Does anyone know of a way that this has been done with rails, but not using EventMachine as a background job? In the end I really just need to know that this is possible.
-L
Inside feeds I want to inspect the pushed content, and then write the results of that to another controller that actually has a model and will persist it.
Why not do all the work in the one controller? If you're trying to separate out different concerns, you could even use two models - for instance one to do the inspecting/parsing and one to handle the persisting. But rarely would you need or want to to pass data from controller to controller.
I am not certain how to include EventMachine in a request/response cycle.
Never used superfeedr myself, but glanced at the docs quickly - are you using the XMPP or PubSubHubbBub client? I assume the latter? If so, you want to do the persistence (and any other time-consuming process) async (outside the request/resp cycle), right?
If you are using an EventMachine-based webserver such as Thin, basically every request cycle is run within an EM reactor. So you can make use of EM's facilities for offloading tasks such as the deferred thread pool. For an example of this in action, check out Enigmamachine, in particular here. (I believe that in addition your db client library needs to be asynchronous.)

Best way to send an email upon creation of a new model instance in Rails?

I have an app with the following models: User, Task, and Assignment. Each Assignment belongs_to a User and a Task (or in other words, a Task is assigned to a User via an Assignment).
Once a User completes a Task, the Assignment is marked as complete, and the app immediately creates a new Assignment (or in other words, assigns the task to someone else).
Immediately after creating this new Assignment, I want to send an email to the new assignee. I know I can do this one of three ways:
Explicitly send the email in my controller.
Send the email in a callback on the Assignment model.
Create an observer on the Assignment model and send the email in after_create.
Which of these options do people think is best, and why? #1 seems bad to me, because I don't want to have to remember to send it in every action that might complete an Assignment. I've heard a couple people say that Rails observers are bad and should be avoided, but I'm not sure if they're people I should trust or not. Any other opinions?
You're right, the first way isn't a good approach. Observers are my preferred way to go, for a couple reasons.
First, if you use TDD (test-driven development) you can shut off observers to more purely test the model without every creation firing off a mailer creation. Then you can unit test the mailer and observer separately.
Second, the idea of separating callbacks creates cleaner code. Callbacks aren't really part of your model, they are events. Your model contains the functions and attributes necessary to run itself, and the callbacks (implemented with observers) are separate event handlers.
That said, I don't think your second option is "bad" or less professional. Either way works as long as it's at the model level, instead of controllers or (even worse) views.
i would go for observers as they reduce clutter in your model / controller code and i can think of no downside in using them ...
iirc sending an email after save email is even an example in the active record observers documentation
You can also do a combination of things. You could use observers for one action, and if there is just a single email for one other action you could use option #1 for it.
Have you heard of acts_as_state_machine, or any other similar solutions?
http://github.com/rubyist/aasm
They allow you to define a state of each object and different things that can happen with state changes.
This allows you to have as much logic as you need about when things are sent, if you need this much. Can be overkill, but can be really handy. I suggest because you want an email sent when a task is 'completed' which sounds like it may be a type of state or status column in your Task model.
In the end, I like this implementation http://www.scottw.com/resque-mail-queue-gem

Equivalent of Django Signals for Rails?

In Rails, the closest I've seen to Django Signals are Observers. The problem with them is that they're restricted to triggering callbacks on hardcoded events related to a model's lifecycle.
Django signals can be created anywhere, triggered anywhere and handled anywhere. The model lifecycle callbacks are just regular signals that happen to come built-in and that are triggered by the ORM.
Does anyone know of a similarly general solution for Rails? It could be some generic Ruby library, not tied to Rails, which would be even better.
Edit: Observer is the closest thing, but it's not what I'm looking for. It's a one-to-many solution. Anyone can listen, but only the originating object can post. I'd like something where you declare a signal, and anyone can trigger it as well as handle it. Also, I don't like the fact that the Ruby Observer dictates that the handler have an #update method. I'd like to be able to pass any method reference with the appropriate signature.
I could use the Ruby Observer to implement my own such broker, but I'm trying to learn if someone already did it.
I think a closer equivalent than Rails' Observer is the standard Ruby Observable module. It lets you add a list of observers to an object and the object can then send notifications to the observers when it changes.
What about the 'wisper' gem? https://github.com/krisleech/wisper
Wisper is a Ruby library for decoupling and managing the dependencies
of your Ruby objects using Pub/Sub.
It is commonly used as an alternative to ActiveRecord callbacks and
Observers to reduce coupling between data and domain layers.
Perhaps acts_as_state machine will help. Most of this functionality has recently been baked into Rails edge.
I just implemented a gem with that. https://github.com/pkoch/django_signal/
Ruby gem 'watchable' is the most appropriate choice
https://github.com/jbarnette/watchable
It has a syntax that is very familiar to Django's (and other frameworks, like Qt and many others).

ActionMailer best practices: Call method in the model or the controller?

Sending an email is usually called after an action on a model, but the email itself is a view operation. I'm looking for how you think about what question(s) to ask yourself to determine where to put the action mailer method call.
I've seen/used them:
In a model method - bad coupling of related but seperate concerns?
In a callback in the model (such as after_save) - best separation as far as I can tell with my current level of knowledge.
In the controller action - just feels wrong, but are there situations were this would be the smartest way to structure the code?
If I want to know how to program I need to think like a programmer, so learning how you go about thinking through particular programming solutions is worth months of coding on my own in isolation. Thank you!
Late answer, but I want to rationalize on the subject:
Usually, in a web app, you want to send emails either as a direct reaction to a client. Or as a background task, in case we're talking about a newsletter/notification mail sort of thing.
The model is basically a data storage mapper. Its logic should encapsulate data-handling/communication with data storage handling. Therefore, inserting logic which does not relate to it is a bit tricky, and in most cases wrong. Let us take the example: User registers an account and should receive a confirmation email. In this case one could say, the confirmation email is a direct effect of the creation of a new account. Now, instead of doing it in the web app, try to create a user in the console. Sounds wrong to trigger a callback in that case, right? So, callback option scratched. Should we still write the method in the model? Well, if it's a direct effect of a user action/input, then it should stay in that workflow. I would write it in the controller after the user was successfully created. Directly. Replicating this logic in the model to be called in the controller anyways adds unnecessary modularity, and dependency of an Active Record model from Action Mailer. Try to consider sharing the model over many apps, in which some of them don't want Action Mailer for it. For the stated reasons, I'm of the opinion that the mailer calls should be where they make sense, and usually the model is not that place. Try to give me examples where it does make.
Well, depends.
I've used all of those options and your point about 'why should I put this where?' is good.
If it's something I want to happen every time a model is updated in a certain way, then I put it in the model. Maybe even in a callback in the model.
Sometimes you're just firing off a report; there's no updating of anything. In that case, I've normally got a resource with an index action that sends the report.
If the mailer isn't really related to the model that's being changed, I could see putting it in a callback. I don't do that very often. I'd be more likely to still encapsulate it in the model. I've done it, just not very often.
I'm aware it's been a while but best practices never die, right? :)
Email is by definition asynchronous communication (except for confirmation email, but even this one it should be a best practice to leave a delay before having to confirm).
Hence in my opinion, the most logical way to send it is :
in a background action (using Sidekiq or delayed_job)
in a callback method : "hey this action is successfully done, maybe we can tell the world now?"
Problem in Rails is that it is not too many callbacks (as in JS for instance): I personnaly find it dirty to have code like:
after_save :callback
def callback
if test_that_is_true_once_in_the_objects_life
Mailer.send_email()
end
end
So, if you really want to think like a programmer, the idea would be to set up some custom callback system in your app.
Eg.
def run_with_callback(action, callback_name)
if send(action)
delay.send(callback_name)
end
end
Or even creating an event system in your app would be a decent solution.
But in the end those solutions are pretty expensive in time so people end-up writing it inline after the action
def activate
[...]
user.save
Mailer.send_mail
respond_to
[...]
end
which is the closest fashion to callback in synchronous programming and results having Mailers call everywhere (in Model and in Controller).
There's several reasons why controllers are a good place for the mailers:
Emails that have nothing to do with a model.
If your emails depend on several models that dont know about each other.
Extracting models to an API should not mean reimplementing mailers.
Mailer content determined by request variables that you dont want to pass to the model.
If your business model requires a lot of diferent emails, model callbacks can stack.
If the email does not depend on the result of model computations.

Resources