pk-merb_messenger 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2008 Michael Klishin
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README ADDED
@@ -0,0 +1,270 @@
1
+ merb_messenger
2
+ ==============
3
+
4
+ A plugin for the Merb framework that provides
5
+ messaging and notifications functionality.
6
+
7
+ The goal is to create a simple transport agnostic
8
+ library that can be used for Jabber, Growl, HTTP,
9
+ mail and other notifications with minimal effort
10
+ in web controllers.
11
+
12
+ Actors
13
+ ==============
14
+
15
+ Core actors of this library are messaging controllers
16
+ and transports. Messaging controllers are called
17
+ from web controllers that handle incoming requests,
18
+ render message templates, do messaging-related
19
+ filtering and pass the result to transports to deliver.
20
+
21
+ Note that messaging controllers share rendering and
22
+ filtering code with Merb core's AbstractController so
23
+ no wheel is invented.
24
+
25
+ Transports and possible use cases
26
+ =====================================
27
+
28
+ What transports do is up to specific implementation:
29
+
30
+ * Jabber transport will deliver messages over XMPP
31
+ * AMQP transport will talk to AMQP broker like RabbitMQ or ZeroMQ
32
+ * mail transport will use SMTP or sendmail to send emails
33
+ * HTTP POST transport will post data over HTTP ("web hooks")
34
+ * Specific HTTP transport may post to Facebook, Twitter, LiveJournal or whatever it may be
35
+ * IRC transport will flood some IRC channel.
36
+ * SMS transport will deliver SMS one way or another
37
+ * XMP-RPC transport will call some WS-* deathstars
38
+ * Some futuristic crazy transport will send Rubinius VM bytecode
39
+ over the wire to be executed on the other end
40
+
41
+
42
+ How to use this prototype
43
+ ==============================
44
+
45
+ Messaging controllers are used from web controllers
46
+ (that handle requests) and can render templates,
47
+ have filters and all that. Transports can be used
48
+ directly in models and not supposed to render
49
+ (at least at this point in evolution of merb messenger).
50
+
51
+ Transports must be required explicitly at this point. Do it
52
+ in init.rb or add config/messengers.rb and load it from init.rb,
53
+ and require transport file there. This is because otherwise
54
+ lazy loading can only be accomplished using Kernel#autoload
55
+ which some people do not like (for instance, it is not thread
56
+ safe in MRI).
57
+
58
+ Say we have a group journal/blog and we want to be notified
59
+ on updates using Jabber. Our web controller action may
60
+ look like this:
61
+
62
+ class Entries < Application
63
+ def create(entry)
64
+ @entry = Entry.new(entry)
65
+ if @entry.save
66
+ run_later do
67
+ JournalMessenger.dispatch(:create, self, :entry => @entry)
68
+ end
69
+ redirect url(:entries)
70
+ else
71
+ @entries = Entry.all
72
+ render :index
73
+ end
74
+ end
75
+ end
76
+
77
+ As you can see, messaging controller is explicitly called from web controller
78
+ using dispatch method. That method takes an action as a symbol, web controller
79
+ instance (to have access to request parameters and session) and a Hash of extra
80
+ options you'd like to pass to it.
81
+
82
+ Now lets look at our messenger:
83
+
84
+ class JournalMessenger < Merb::MessagingController
85
+ delivery_options :lan_growl,
86
+ :notification => "journal notification",
87
+ :title => "New journal entry posted"
88
+
89
+ delivery_options :remote,
90
+ :title => "New journal entry posted",
91
+ :to => ["app.notifications@jabber.org"],
92
+ :message_type => :chat
93
+
94
+ def create(options)
95
+ # render the template in app/messengers/views/journal/create.text.erb
96
+ body = render(:index)
97
+
98
+ # deliver using XMPP
99
+ # transport is called :remote
100
+ # in the application
101
+ deliver :remote, :body => body
102
+ # deliver using Growl transport
103
+ # that is called :lan_growl across
104
+ # the app
105
+ deliver :lan_growl, :body => body
106
+ end
107
+ end
108
+
109
+ Every messenger action takes a hash of options that includes web controller
110
+ parameters and additional options you passed on dispatch.
111
+
112
+ Every messenger uses +deliver+ method to deliver notifications instantly
113
+ and (in the future) +queue+ method to stack a notification in a queue to
114
+ be delivered later. Queueing functionality will be 100% ORM agnostic.
115
+
116
+ This plugin is all about connecting your web controllers and transports.
117
+ It supposed to be generic. Because message transports are very
118
+ different, it's up to particular transport what
119
+ options delivery and queue methods would take.
120
+
121
+ To eliminate the need to constantly duplicate some common options (like "from",
122
+ "title" and similar), Merb messenger lets you set them up messenger-wide:
123
+
124
+ delivery_options :remote,
125
+ :title => "Journal event",
126
+ :to => ["app.notifications@jabber.org"],
127
+ :message_type => :chat
128
+
129
+ Above we say that all messages that go via transport called "remote"
130
+ (that is XMPP transport in this example) use title "Journal event",
131
+ delivered to "app.notifications@jabber.org" and use message type
132
+ "chat".
133
+
134
+ +deliver+ method's options are merged with class-wide delivery options
135
+ before they are passed on to transport. Obviously, former take
136
+ precedence over the latter.
137
+
138
+ Implementing transports
139
+ ========================
140
+
141
+ Transport API is simple and may be as little work as implementing a
142
+ single method, deliver. Some transports that need to establish connection and
143
+ handle disconnection may add a few more methods: connect, reconnect, disconnect.
144
+ Those providing queuing functionality may add queue method that acts as deliver
145
+ but does not do immediate delivery.
146
+
147
+ Here is a Jabber transport implementation that uses xmpp4r:
148
+ require 'xmpp4r/client'
149
+
150
+ module Merb
151
+ module Messenger
152
+ class Xmpp4rTransport
153
+ include ::Jabber
154
+
155
+ class_inheritable_accessor :_default_delivery_options
156
+ self._default_delivery_options = Mash.new(:message_type => :chat)
157
+
158
+ def self.delivery_options(options)
159
+ self._default_delivery_options = options
160
+ end
161
+
162
+ attr_reader :transport
163
+
164
+ def initialize(options)
165
+ @transport = Client.new(options[:jid])
166
+ @transport.connect
167
+
168
+ @transport.auth(options[:password])
169
+ end
170
+
171
+ def connect
172
+ self.transport.connect
173
+ end
174
+
175
+ def disconnect
176
+ self.transport.close
177
+ end
178
+
179
+ def deliver(options = {})
180
+ options = _default_delivery_options.merge(options)
181
+
182
+ m = Message.new(options[:to], options[:body])
183
+
184
+ m.set_subject(options[:subject]) if options[:subject]
185
+ m.set_subject(options[:id]) if options[:id]
186
+ m.set_type(options[:message_type] || :normal)
187
+
188
+ self.transport.send(m)
189
+ end
190
+ end
191
+ end
192
+ end
193
+
194
+
195
+ Setting transports up
196
+ =======================
197
+
198
+ Transports are usually set up before Merb loads your application
199
+ classes, but it's obviously neither a requirement, nor a convention.
200
+ It just makes messaging classes loaded before code that uses them loads.
201
+
202
+ To set up a transport you use
203
+ Merb::Messenger.setup_transport method that takes transport name
204
+ (can be anything, pick how you'd like to address that transport in your
205
+ application), transport type alias (:growl, :xmpp4r, can be anything people
206
+ already written transports for) and a Hash of options.
207
+
208
+ Since transports are so different by nature (Growl has host, port, password,
209
+ notifications list and application name options, XMPP usually has jid, server,
210
+ port, SASL/TLS options and password, smpt transport would have SMTP server
211
+ settings, sendmail would have a sendmail path, etc), possible options are
212
+ up to transport authors.
213
+
214
+ Merb::BootLoader.before_app_loads do
215
+ Merb::Messenger.setup_transport(:lan_growl, :growl, {
216
+ :application => "merbpack",
217
+ :notifications => ["journal_notification"]
218
+ })
219
+ end
220
+
221
+ You can use transports directly if you need to. For instance,
222
+ you can set up a Jabber transport that's gonna notify you
223
+ when Merb worker is shut down or restarted. This way you
224
+ can be notified on crashes and application deployments.
225
+
226
+ Here is an example:
227
+
228
+ Merb::BootLoader.after_app_loads do
229
+ im = Merb::MessagingController.message_transport :lan_growl
230
+
231
+ im.deliver({
232
+ :title => "MerbPack presence",
233
+ :body => "MerbPack agent is back online",
234
+ :notification => "presence notification"
235
+ })
236
+ end
237
+
238
+
239
+ Merb::BootLoader.before_master_shutdown do
240
+ Merb.logger.info "MerbPack agent is going offline..."
241
+ im = Merb::MessagingController.message_transport :lan_growl
242
+
243
+ im.deliver({
244
+ :title => "MerbPack presence",
245
+ :body => "MerbPack agent is about to shut down",
246
+ :notification => "presence notification"
247
+ })
248
+ end
249
+
250
+ Merb::BootLoader.before_worker_shutdown do
251
+ Merb.logger.info "Shutting down worker..."
252
+
253
+ Merb::MessagingController.message_transport(:remote).disconnect
254
+ end
255
+
256
+
257
+ Here we use two transports named :lan_growl and :remote. First uses
258
+ Growl transport in local area network, and second is a Jabber transport
259
+ that sends XMPP notifications.
260
+
261
+ We use them to notify people when application goes down and comes back
262
+ up online.
263
+
264
+ The Future
265
+ ===================
266
+
267
+ Merb messenger will be part of Merb 1.1 in early 2009. The Holy Grails of
268
+ this plugin is to provide simple generic interface for messaging of all
269
+ kinds: from Jabber and mail notifications to talking to AMQP brokers
270
+ like RabbitMQ or ZeroMQ, doing "web hooks" and so forth.
data/Rakefile ADDED
@@ -0,0 +1,51 @@
1
+ require 'rubygems'
2
+ require 'rake/gempackagetask'
3
+
4
+ require 'merb-core'
5
+ require 'merb-core/tasks/merb'
6
+
7
+ GEM_NAME = "merb_messenger"
8
+ GEM_VERSION = "0.0.1"
9
+ AUTHOR = "Michael Klishin"
10
+ EMAIL = "michael@novemberain.com"
11
+ HOMEPAGE = "http://merbivore.com/"
12
+ SUMMARY = "Merb plugin that provides messaging/notifications functionality: from email and XMPP to AMPQ and beyond."
13
+
14
+ spec = Gem::Specification.new do |s|
15
+ s.rubyforge_project = 'merb'
16
+ s.name = GEM_NAME
17
+ s.version = GEM_VERSION
18
+ s.platform = Gem::Platform::RUBY
19
+ s.has_rdoc = false
20
+ s.extra_rdoc_files = ["README", "LICENSE", 'TODO']
21
+ s.summary = SUMMARY
22
+ s.description = SUMMARY
23
+ s.author = AUTHOR
24
+ s.email = EMAIL
25
+ s.homepage = HOMEPAGE
26
+ s.add_dependency('merb-core', '>= 1.1')
27
+ s.require_path = 'lib'
28
+ s.files = %w(LICENSE README Rakefile TODO) + Dir.glob("{lib,spec}/**/*")
29
+
30
+ end
31
+
32
+ Rake::GemPackageTask.new(spec) do |pkg|
33
+ pkg.gem_spec = spec
34
+ end
35
+
36
+ desc "install the plugin as a gem"
37
+ task :install do
38
+ Merb::RakeHelper.install(GEM_NAME, :version => GEM_VERSION)
39
+ end
40
+
41
+ desc "Uninstall the gem"
42
+ task :uninstall do
43
+ Merb::RakeHelper.uninstall(GEM_NAME, :version => GEM_VERSION)
44
+ end
45
+
46
+ desc "Create a gemspec file"
47
+ task :gemspec do
48
+ File.open("#{GEM_NAME}.gemspec", "w") do |file|
49
+ file.puts spec.to_ruby
50
+ end
51
+ end
data/TODO ADDED
@@ -0,0 +1,9 @@
1
+ * Finally put spec suite I have online.
2
+ * Add more spec examples.
3
+ * Add event more spec examples.
4
+ * F*cking spec examples!!
5
+ * Mail transport.
6
+ * HTTP transport (should use Tony Arcieri's FastHTTP library).
7
+ * Queueing functionality for existing transports.
8
+ * AMQP integration.
9
+ * Extensions for RPC, WS-* deathstars and the rest of existing ecosystem.
@@ -0,0 +1,43 @@
1
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
2
+
3
+ # make sure we're running inside Merb
4
+ if defined?(Merb::Plugins)
5
+ # Register path for messengers under app/messengers
6
+ Merb.push_path(:messenger, Merb.root / "app"/ "messengers")
7
+
8
+ require "merb_messenger/messaging_controller"
9
+ require "merb_messenger/messaging_mixin"
10
+
11
+ module Merb
12
+ module Messenger
13
+ @@test_deliveries = Hash.new([])
14
+
15
+ def self.clean_test_deliveries!
16
+ @@test_deliveries = Hash.new([])
17
+ end
18
+
19
+ def self.test_deliveries
20
+ @@test_deliveries
21
+ end
22
+
23
+ def setup_transport(name, type, options)
24
+ begin
25
+ # Try to load custom Transports from project/merb/merb_messenger/transports
26
+ require Merb.root / 'merb' / 'merb_messenger' / 'transports' / type.to_s
27
+ rescue LoadError => e
28
+ # Try to load bundled Transports
29
+ require "merb_messenger/transports/#{type.to_s}"
30
+ end
31
+
32
+ ::Merb::MessagingController.message_transport(name, type, options)
33
+ end
34
+
35
+ module_function :setup_transport
36
+ end
37
+ end
38
+
39
+ Merb::Plugins.config[:merb_messenger] = {
40
+ }
41
+
42
+ Merb::Plugins.add_rakefiles "merb_messenger/merbtasks"
43
+ end
@@ -0,0 +1,6 @@
1
+ namespace :merb_messenger do
2
+ desc "Do something for merb_messenger"
3
+ task :default do
4
+ puts "merb_messenger doesn't do anything"
5
+ end
6
+ end
@@ -0,0 +1,106 @@
1
+ module Merb
2
+ class MessagingController < AbstractController
3
+ #include Merb::Messenger::Transport
4
+
5
+ attr_reader :params, :web_controller
6
+
7
+ class_inheritable_accessor :_message_transports, :_default_delivery_options
8
+
9
+ self._message_transports = Mash.new
10
+ self._default_delivery_options = Mash.new
11
+
12
+ cattr_accessor :_subclasses
13
+ self._subclasses = Set.new
14
+
15
+
16
+ render_options :format => :text
17
+
18
+ def self.subclasses_list
19
+ _subclasses
20
+ end
21
+
22
+ def _template_location(action, type = :text, controller = controller_name)
23
+ "#{controller}/#{action}.#{type}"
24
+ end
25
+
26
+ def _absolute_template_location(template, type)
27
+ template.match(/\.#{type.to_s.escape_regexp}$/) ? template : "#{template}.#{type}"
28
+ end
29
+
30
+ def self.inherited(klass)
31
+ _subclasses << klass.to_s
32
+ super
33
+
34
+ klass._template_root ||= Merb.dir_for(:messenger) / "views"
35
+ end
36
+
37
+
38
+ def self.dispatch(message, web_controller, options = {}, msg_options = {})
39
+ self.new(web_controller, options).__send__(message, msg_options)
40
+ end
41
+
42
+ def self.message_transport(name = :default, type = nil, options = nil)
43
+ return self._message_transports[name] if type.blank?
44
+
45
+ klass = Merb::Messenger.const_get("#{type.to_s.camel_case}Transport")
46
+ t = klass.new(options)
47
+ self._message_transports[name] = t
48
+ end
49
+
50
+ def self.delivery_options(name = :default, value = nil)
51
+ return self._default_delivery_options[name] unless value
52
+ self._default_delivery_options[name] = value
53
+ end
54
+
55
+
56
+
57
+ def initialize(web_controller, opts = {})
58
+ @web_controller = web_controller
59
+ @params = Mash.new(@web_controller.params.dup)
60
+
61
+ @params.merge!(opts) unless opts.blank?
62
+ super
63
+
64
+ @content_type = @web_controller.content_type
65
+ end
66
+
67
+ def transport
68
+ self.class._transport
69
+ end
70
+
71
+ def deliver(client = :default, options = {})
72
+ opts = (self._default_delivery_options[client] || {}).
73
+ merge(self.params).
74
+ merge(options)
75
+
76
+ [opts[:to]].flatten.each do |to|
77
+ self._message_transports[client].deliver(opts.merge(:to => to))
78
+ end
79
+ end
80
+
81
+ def session
82
+ self.web_controller.request.session rescue {}
83
+ end
84
+
85
+ def request
86
+ self.web_controller.request
87
+ end
88
+
89
+ # Mimic the behavior of absolute_url in AbstractController
90
+ # but use @web_controller.request
91
+ def url(name, *args)
92
+ return web_controller.url(name, *args) if web_controller
93
+ super
94
+ end
95
+
96
+ alias_method :relative_url, :url
97
+
98
+ # Mimic the behavior of absolute_url in AbstractController
99
+ # but use @web_controller.request
100
+ def absolute_url(name, *args)
101
+ return web_controller.absolute_url(name, *args) if web_controller
102
+ super
103
+ end
104
+
105
+ end
106
+ end
@@ -0,0 +1,7 @@
1
+ module Merb
2
+ module MessagingMixin
3
+
4
+
5
+
6
+ end
7
+ end
@@ -0,0 +1,32 @@
1
+ require 'ruby-growl'
2
+
3
+ module Merb
4
+ module Messenger
5
+ class GrowlTransport
6
+ class_inheritable_accessor :_default_delivery_options
7
+ self._default_delivery_options = Mash.new
8
+
9
+ def self.delivery_options(options)
10
+ self._default_delivery_options = options
11
+ end
12
+
13
+ attr_reader :transport
14
+
15
+ def initialize(options)
16
+ @transport = ::Growl.new(options[:host], options[:application], options[:notifications])
17
+ end
18
+
19
+ def connect
20
+ end
21
+
22
+ def disconnect
23
+ end
24
+
25
+ def deliver(options = {})
26
+ options = _default_delivery_options.merge(options)
27
+
28
+ self.transport.notify(options[:notification], options[:title], options[:body])
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,92 @@
1
+ module Merb
2
+ module Messenger
3
+ class MailTransport
4
+ attr_reader :delivery_strategy, :composition_strategy
5
+
6
+ class_inheritable_accessor :_default_delivery_options
7
+ self._default_delivery_options = Mash.new(:message_type => :chat)
8
+
9
+ def self.delivery_options(options)
10
+ self._default_delivery_options = options
11
+ end
12
+
13
+ def initialize(options)
14
+ @delivery_strategy = options[:deliver_with] || :sendmail
15
+ @composition_strategy = options[:compose_with] || :mailfactory
16
+ end
17
+
18
+ def deliver(options = {})
19
+ options = _default_delivery_options.merge(options)
20
+
21
+ email = case self.composition_strategy
22
+ when :mailfactory then compose_with_mailfactory(options)
23
+ when :tmail then compose_with_tmail(options)
24
+ when Proc then self.composition_strategy.call(options)
25
+ when Class then compose_with_custom_strategy(options)
26
+ else
27
+ self.send "compose_with_#{self.composition_strategy}"
28
+ end
29
+
30
+ case self.delivery_strategy
31
+ when :sendmail, :send_mail then deliver_with_sendmail(email, options)
32
+ when :smtp, :netsmtp, :net_smtp then deliver_with_smtp(email, options)
33
+ when Proc then self.delivery_strategy.call(email, options)
34
+ when Class then deliver_with_custom_strategy(email, options)
35
+ else
36
+ self.send "deliver_with_#{self.delivery_strategy}"
37
+ end
38
+ end
39
+
40
+ protected
41
+
42
+ def compose_with_mailfactory(options)
43
+ email = MailFactory.new
44
+ email.from = options[:from]
45
+
46
+ [options[:reply_to] || []].flatten.each do |address|
47
+ email.add_header "Reply-To", address
48
+ end
49
+ [options[:to] || []].flatten.each do |address|
50
+ email.add_header "To", address
51
+ end
52
+ [options[:cc] || []].flatten.each do |address|
53
+ email.add_header "Cc", address
54
+ end
55
+
56
+ email.subject = options[:subject] if options[:subject]
57
+ email.text = options[:text] if options[:text]
58
+ email.html = options[:html] if options[:html]
59
+
60
+ email
61
+ end
62
+
63
+ def compose_with_tmail(options)
64
+ # TODO
65
+ end
66
+
67
+ def compose_with_custom_strategy(options)
68
+ # TODO
69
+ end
70
+
71
+ def deliver_with_sendmail(email, options)
72
+ if Merb.testing?
73
+ Merb::Messenger.test_deliveries[:email].push email
74
+ else
75
+ recepients = [options[:to] || []].flatten.join(',')
76
+ Merb.logger.info! "Sending email with sendmail #{recepients}"
77
+ IO.popen("sendmail #{recepients}", 'w+') do |sendmail|
78
+ sendmail.puts email.to_s
79
+ end
80
+ end
81
+ end # deliver_with_sendmail
82
+
83
+ def deliver_with_smtp(email, options)
84
+ # TODO
85
+ end
86
+
87
+ def deliver_with_custom_strategy(email, options)
88
+ # TODO
89
+ end # deliver_with_custom_strategy
90
+ end
91
+ end
92
+ end
@@ -0,0 +1,45 @@
1
+ require 'xmpp4r/client'
2
+
3
+ module Merb
4
+ module Messenger
5
+ class Xmpp4rTransport
6
+ include ::Jabber
7
+
8
+ class_inheritable_accessor :_default_delivery_options
9
+ self._default_delivery_options = Mash.new(:message_type => :chat)
10
+
11
+ def self.delivery_options(options)
12
+ self._default_delivery_options = options
13
+ end
14
+
15
+ attr_reader :transport
16
+
17
+ def initialize(options)
18
+ @transport = Client.new(options[:jid])
19
+ @transport.connect
20
+
21
+ @transport.auth(options[:password])
22
+ end
23
+
24
+ def connect
25
+ self.transport.connect
26
+ end
27
+
28
+ def disconnect
29
+ self.transport.close
30
+ end
31
+
32
+ def deliver(options = {})
33
+ options = _default_delivery_options.merge(options)
34
+
35
+ m = Message.new(options[:to], options[:body])
36
+
37
+ m.set_subject(options[:subject]) if options[:subject]
38
+ m.set_subject(options[:id]) if options[:id]
39
+ m.set_type(options[:message_type] || :normal)
40
+
41
+ self.transport.send(m)
42
+ end
43
+ end
44
+ end
45
+ end
metadata ADDED
@@ -0,0 +1,76 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: pk-merb_messenger
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Michael Klishin
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2009-09-15 00:00:00 -07:00
13
+ default_executable:
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: merb-core
17
+ type: :runtime
18
+ version_requirement:
19
+ version_requirements: !ruby/object:Gem::Requirement
20
+ requirements:
21
+ - - ">="
22
+ - !ruby/object:Gem::Version
23
+ version: "1.1"
24
+ version:
25
+ description: "Merb plugin that provides messaging/notifications functionality: from email and XMPP to AMPQ and beyond."
26
+ email: michael@novemberain.com
27
+ executables: []
28
+
29
+ extensions: []
30
+
31
+ extra_rdoc_files:
32
+ - README
33
+ - LICENSE
34
+ - TODO
35
+ files:
36
+ - LICENSE
37
+ - README
38
+ - Rakefile
39
+ - TODO
40
+ - lib/merb_messenger
41
+ - lib/merb_messenger/merbtasks.rb
42
+ - lib/merb_messenger/messaging_controller.rb
43
+ - lib/merb_messenger/messaging_mixin.rb
44
+ - lib/merb_messenger/transports
45
+ - lib/merb_messenger/transports/growl.rb
46
+ - lib/merb_messenger/transports/mailfactory.rb
47
+ - lib/merb_messenger/transports/xmpp4r.rb
48
+ - lib/merb_messenger.rb
49
+ has_rdoc: false
50
+ homepage: http://merbivore.com/
51
+ post_install_message:
52
+ rdoc_options: []
53
+
54
+ require_paths:
55
+ - lib
56
+ required_ruby_version: !ruby/object:Gem::Requirement
57
+ requirements:
58
+ - - ">="
59
+ - !ruby/object:Gem::Version
60
+ version: "0"
61
+ version:
62
+ required_rubygems_version: !ruby/object:Gem::Requirement
63
+ requirements:
64
+ - - ">="
65
+ - !ruby/object:Gem::Version
66
+ version: "0"
67
+ version:
68
+ requirements: []
69
+
70
+ rubyforge_project: merb
71
+ rubygems_version: 1.2.0
72
+ signing_key:
73
+ specification_version: 3
74
+ summary: "Merb plugin that provides messaging/notifications functionality: from email and XMPP to AMPQ and beyond."
75
+ test_files: []
76
+