From 9d0541d2fe872551ad0bdcc86c8f5f0f11d9b497 Mon Sep 17 00:00:00 2001 From: Timur Vafin Date: Mon, 11 Jul 2016 00:16:53 +0300 Subject: [PATCH 1/3] Add example of notifications MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Example how to implement notification system using multiple delivery system. In out example we use Email and Hipchat. There are two main things to learn: * SendFeedback interactor — the entry point * FeedbackNotification model — class to generate content of different type of notifications --- README.md | 1 + notifications/README.md | 175 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 176 insertions(+) create mode 100644 notifications/README.md diff --git a/README.md b/README.md index 47839e6..cbe2126 100644 --- a/README.md +++ b/README.md @@ -3,3 +3,4 @@ * [Feedback form](feedback-form) * [Markdown preview](markdown-preview) * [Image upload with preview](image-upload-with-preview) +* [Notification](notifications) diff --git a/notifications/README.md b/notifications/README.md new file mode 100644 index 0000000..027734d --- /dev/null +++ b/notifications/README.md @@ -0,0 +1,175 @@ +# Notification + +In case we would like to to send notifications using multiple channels like Email +and HipChat right after some business action (send feedback for example). + +The entry point is `SendFeedback` organizer which will organize interactors for: +create feedback, send notification via Email, and send notification via HipChat. + +All notification will be pushed in the background using `ActiveJob`. + +```ruby +# app/interactors/send_feedback.rb +class SendFeedback + include Interactor::Organizer + + organize CreateFeedback, NotifyUserViaEmail, NotifyUserViaHipchat +end + +# app/interactors/notify_user_via_email.rb +class NotifyUserViaEmail + include Interactor + + def call + ApplicationMailer.feedback_notification(email, message.content_for(:email)).deliver_later + end + + private + + def email + context.feedback.employee.email + end + + def message + FeedbackNotification.new(context.feedback) + end +end + +# app/interactors/notify_user_via_hipchat.rb +class NotifyUserViaHipchat + include Interactor + + class Job < ActiveJob::Base + include Rollbar::ActiveJob + + def perform(email, message) + HipchatApi.new.private_message(email, message) + end + end + + def call + Job.perform_later(email, message.content_for(:hipchat)) + end + + private + + def email + context.feedback.employee.email + end + + def message + FeedbackNotification.new(context.feedback) + end +end +``` + +Content for each notification is generated using `FeedbackNotification` class. + +Gem `render_anywhere` used in this example, but could be replaced with native Rails 5 +feature `ActionController::Renderer` later. + +```ruby +# app/controllers/application_rendering_controller.rb +class ApplicationRenderingController < RenderAnywhere::RenderingController + def default_url_options + { host: ENV.fetch("HOST") } + end +end + +# app/models/fedback_notification.rb +class FeedbackNotification + include RenderAnywhere + + attr_reader :feedback, :rendering_controller + private :feedback, :rendering_controller + + def initialize(feedback) + @feedback = feedback + @rendering_controller ||= ApplicationRenderingController.new + end + + def content_for(type) + render( + template: template_for(type), + layout: false, + locals: locals + ) + end + + private + + def template_for(type) + "feedback_notification/#{type}", + end + + def locals + { + feedback: feedback.decorate + } + end +end + +# app/views/feedback_notification/email.html.slim +p + | You have new feedback from #{feedback.full_name_with_email}. + + p + blockquote = feedback.text + +# app/views/feedback_notification/hipchat.html.slim +p + | You have new feedback from #{feedback.full_name_with_email}. + +br + + p + em = feedback.text +``` + +Here are examples of the wrapper for HipChat API and `ApplicationMailer` + +```ruby +# app/models/hipchat_api.rb +class HipchatApi + attr_reader :client + private :client + + def initialize(client: build_client) + @client = client + end + + def private_message(email, message, format = "html") + client.user(email).send(message, format) + rescue HipChat::UnknownUser => e + Rails.logger.error("HipChat: #{e.message}") + end + + private + + def build_client + HipChat::Client.new(ENV.fetch("HIPCHAT_TOKEN"), api_version: "v2") + end +end + +# app/mailers/application_mailer.rb +class ApplicationMailer < ActionMailer::Base + default from: ENV.fetch("MAILER_SENDER") + + def feedback_notification(email, body) + mail( + body: body, + content_type: "text/html", + to: email, + subject: "You have new Feedback" + ) + end +end +``` + +```ruby +# Gemfile +gem "hipchat" +gem "interactor" +gem "render_anywhere" +gem "sidekiq" +``` From 603cecd75abeb3f16ead0fbd5a449fd11f8d45a4 Mon Sep 17 00:00:00 2001 From: Timur Vafin Date: Tue, 12 Jul 2016 11:24:03 +0300 Subject: [PATCH 2/3] Use delegators --- notifications/README.md | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/notifications/README.md b/notifications/README.md index 027734d..fce36bd 100644 --- a/notifications/README.md +++ b/notifications/README.md @@ -20,18 +20,17 @@ end class NotifyUserViaEmail include Interactor + delegate :feedback, to: :context + delegate :email, to: feedback.employee + def call ApplicationMailer.feedback_notification(email, message.content_for(:email)).deliver_later end private - def email - context.feedback.employee.email - end - def message - FeedbackNotification.new(context.feedback) + FeedbackNotification.new(feedback) end end @@ -47,18 +46,17 @@ class NotifyUserViaHipchat end end + delegate :feedback, to: :context + delegate :email, to: feedback.employee + def call Job.perform_later(email, message.content_for(:hipchat)) end private - def email - context.feedback.employee.email - end - def message - FeedbackNotification.new(context.feedback) + FeedbackNotification.new(feedback) end end ``` From e053f073fc83d1596c9c49193617ea4a555b3bfa Mon Sep 17 00:00:00 2001 From: Timur Vafin Date: Fri, 22 Jul 2016 10:20:04 +0300 Subject: [PATCH 3/3] ADd sidekiq configuration --- notifications/README.md | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/notifications/README.md b/notifications/README.md index fce36bd..1569b4c 100644 --- a/notifications/README.md +++ b/notifications/README.md @@ -162,6 +162,16 @@ class ApplicationMailer < ActionMailer::Base ) end end + +# config/application.rb +module YourApp + class Application < Rails::Application + # Be sure to have the adapter's gem in your Gemfile + # and follow the adapter's specific installation + # and deployment instructions. + config.active_job.queue_adapter = :sidekiq + end +end ``` ```ruby @@ -171,3 +181,4 @@ gem "interactor" gem "render_anywhere" gem "sidekiq" ``` +