-
Notifications
You must be signed in to change notification settings - Fork 0
Attempt to come up with a useable unified messaging interface for Merb
License
michaelklishin/merb-messenger
Folders and files
Name | Name | Last commit message | Last commit date | |
---|---|---|---|---|
Repository files navigation
merb_messenger ============== A plugin for the Merb framework that provides messaging and notifications functionality. The goal is to create a simple transport agnostic library that can be used for Jabber, Growl, HTTP, mail and other notifications with minimal effort in web controllers. Actors ============== Core actors of this library are messaging controllers and transports. Messaging controllers are called from web controllers that handle incoming requests, render message templates, do messaging-related filtering and pass the result to transports to deliver. Note that messaging controllers share rendering and filtering code with Merb core's AbstractController so no wheel is invented. Transports and possible use cases ===================================== What transports do is up to specific implementation: * Jabber transport will deliver messages over XMPP * AMQP transport will talk to AMQP broker like RabbitMQ or ZeroMQ * mail transport will use SMTP or sendmail to send emails * HTTP POST transport will post data over HTTP ("web hooks") * Specific HTTP transport may post to Facebook, Twitter, LiveJournal or whatever it may be * IRC transport will flood some IRC channel. * SMS transport will deliver SMS one way or another * XMP-RPC transport will call some WS-* deathstars * Some futuristic crazy transport will send Rubinius VM bytecode over the wire to be executed on the other end How to use this prototype ============================== Messaging controllers are used from web controllers (that handle requests) and can render templates, have filters and all that. Transports can be used directly in models and not supposed to render (at least at this point in evolution of merb messenger). Transports must be required explicitly at this point. Do it in init.rb or add config/messengers.rb and load it from init.rb, and require transport file there. This is because otherwise lazy loading can only be accomplished using Kernel#autoload which some people do not like (for instance, it is not thread safe in MRI). Say we have a group journal/blog and we want to be notified on updates using Jabber. Our web controller action may look like this: class Entries < Application def create(entry) @entry = Entry.new(entry) if @entry.save run_later do JournalMessenger.dispatch(:create, self, :entry => @entry) end redirect url(:entries) else @entries = Entry.all render :index end end end As you can see, messaging controller is explicitly called from web controller using dispatch method. That method takes an action as a symbol, web controller instance (to have access to request parameters and session) and a Hash of extra options you'd like to pass to it. Now lets look at our messenger: class JournalMessenger < Merb::MessagingController delivery_options :lan_growl, :notification => "journal notification", :title => "New journal entry posted" delivery_options :remote, :title => "New journal entry posted", :to => ["app.notifications@jabber.org"], :message_type => :chat def create(options) # render the template in app/messengers/views/journal/create.text.erb body = render(:index) # deliver using XMPP # transport is called :remote # in the application deliver :remote, :body => body # deliver using Growl transport # that is called :lan_growl across # the app deliver :lan_growl, :body => body end end Every messenger action takes a hash of options that includes web controller parameters and additional options you passed on dispatch. Every messenger uses +deliver+ method to deliver notifications instantly and (in the future) +queue+ method to stack a notification in a queue to be delivered later. Queueing functionality will be 100% ORM agnostic. This plugin is all about connecting your web controllers and transports. It supposed to be generic. Because message transports are very different, it's up to particular transport what options delivery and queue methods would take. To eliminate the need to constantly duplicate some common options (like "from", "title" and similar), Merb messenger lets you set them up messenger-wide: delivery_options :remote, :title => "Journal event", :to => ["app.notifications@jabber.org"], :message_type => :chat Above we say that all messages that go via transport called "remote" (that is XMPP transport in this example) use title "Journal event", delivered to "app.notifications@jabber.org" and use message type "chat". +deliver+ method's options are merged with class-wide delivery options before they are passed on to transport. Obviously, former take precedence over the latter. Implementing transports ======================== Transport API is simple and may be as little work as implementing a single method, deliver. Some transports that need to establish connection and handle disconnection may add a few more methods: connect, reconnect, disconnect. Those providing queuing functionality may add queue method that acts as deliver but does not do immediate delivery. Here is a Jabber transport implementation that uses xmpp4r: require 'xmpp4r/client' module Merb module Messenger class Xmpp4rTransport include ::Jabber class_inheritable_accessor :_default_delivery_options self._default_delivery_options = Mash.new(:message_type => :chat) def self.delivery_options(options) self._default_delivery_options = options end attr_reader :transport def initialize(options) @transport = Client.new(options[:jid]) @transport.connect @transport.auth(options[:password]) end def connect self.transport.connect end def disconnect self.transport.close end def deliver(options = {}) options = _default_delivery_options.merge(options) m = Message.new(options[:to], options[:body]) m.set_subject(options[:subject]) if options[:subject] m.set_subject(options[:id]) if options[:id] m.set_type(options[:message_type] || :normal) self.transport.send(m) end end end end Setting transports up ======================= Transports are usually set up before Merb loads your application classes, but it's obviously neither a requirement, nor a convention. It just makes messaging classes loaded before code that uses them loads. To set up a transport you use Merb::Messenger.setup_transport method that takes transport name (can be anything, pick how you'd like to address that transport in your application), transport type alias (:growl, :xmpp4r, can be anything people already written transports for) and a Hash of options. Since transports are so different by nature (Growl has host, port, password, notifications list and application name options, XMPP usually has jid, server, port, SASL/TLS options and password, smpt transport would have SMTP server settings, sendmail would have a sendmail path, etc), possible options are up to transport authors. Merb::BootLoader.before_app_loads do Merb::Messenger.setup_transport(:lan_growl, :growl, { :application => "merbpack", :notifications => ["journal_notification"] }) end You can use transports directly if you need to. For instance, you can set up a Jabber transport that's gonna notify you when Merb worker is shut down or restarted. This way you can be notified on crashes and application deployments. Here is an example: Merb::BootLoader.after_app_loads do im = Merb::MessagingController.message_transport :lan_growl im.deliver({ :title => "MerbPack presence", :body => "MerbPack agent is back online", :notification => "presence notification" }) end Merb::BootLoader.before_master_shutdown do Merb.logger.info "MerbPack agent is going offline..." im = Merb::MessagingController.message_transport :lan_growl im.deliver({ :title => "MerbPack presence", :body => "MerbPack agent is about to shut down", :notification => "presence notification" }) end Merb::BootLoader.before_worker_shutdown do Merb.logger.info "Shutting down worker..." Merb::MessagingController.message_transport(:remote).disconnect end Here we use two transports named :lan_growl and :remote. First uses Growl transport in local area network, and second is a Jabber transport that sends XMPP notifications. We use them to notify people when application goes down and comes back up online. The Future =================== Merb messenger will be part of Merb 1.1 in early 2009. The Holy Grails of this plugin is to provide simple generic interface for messaging of all kinds: from Jabber and mail notifications to talking to AMQP brokers like RabbitMQ or ZeroMQ, doing "web hooks" and so forth.
About
Attempt to come up with a useable unified messaging interface for Merb
Resources
License
Stars
Watchers
Forks
Releases
No releases published
Packages 0
No packages published