broadcast 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,9 @@
1
+ *.gem
2
+ .bundle
3
+ Gemfile.lock
4
+ pkg/*
5
+ sandbox
6
+ *.log
7
+ coverage
8
+ bin
9
+ .rspec
data/Gemfile ADDED
@@ -0,0 +1,16 @@
1
+ source "http://rubygems.org"
2
+
3
+ gemspec
4
+
5
+ group :development, :test do
6
+ gem 'rspec'
7
+ gem 'rcov'
8
+
9
+ platforms :mri_18 do
10
+ gem "ruby-debug"
11
+ end
12
+
13
+ platforms :mri_19 do
14
+ gem "ruby-debug19"
15
+ end
16
+ end
data/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2011 Marcin Bunsch, Future Simple Inc.
2
+
3
+ Permission is hereby granted, free of charge, to any person
4
+ obtaining a copy of this software and associated documentation
5
+ files (the "Software"), to deal in the Software without
6
+ restriction, including without limitation the rights to use,
7
+ copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ copies of the Software, and to permit persons to whom the
9
+ Software is furnished to do so, subject to the following
10
+ conditions:
11
+
12
+ The above copyright notice and this permission notice shall be
13
+ included in all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
17
+ OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
19
+ HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
20
+ WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21
+ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
22
+ OTHER DEALINGS IN THE SOFTWARE.
data/README.markdown ADDED
@@ -0,0 +1,212 @@
1
+ Broadcast
2
+ =========
3
+
4
+ A broadcasting microframework making publishing of messages to different services easy and DRY.
5
+
6
+ Use Cases
7
+ ------------
8
+
9
+ Possible use cases include:
10
+
11
+ - publishing a update on your product Twitter feed
12
+ - notifying coworkers on Jabber of a deployment when it happens
13
+ - sending update on company IRC when a signup in your startup app happens
14
+ - publishing daily statistics to Yammer
15
+ - sending an email and a Jabber update when a specific threshold is reached (like number of users)
16
+
17
+ Installation
18
+ ------------
19
+
20
+ Broadcast is in alpha state, so it's not pushed to RubyGems yet.
21
+
22
+ You can install it by adding the following line to your Gemfile:
23
+
24
+ gem 'broadcast', :git => 'git://github.com/futuresimple/broadcast.git'
25
+
26
+ and running
27
+
28
+ bundle install
29
+
30
+ Usage
31
+ -----
32
+
33
+ Broadcast has 2 main classes: Medium and Message (hat tip to Marshall McLuhan).
34
+
35
+ **Broadcast::Medium** is the service the message will be sent to, and **Broadcast::Message** is, well, the message.
36
+
37
+ The first thing you need to do is to configure the desired Media. For example, to configure jabber, put something like this in some configuration file (e.g. a Rails initializer):
38
+
39
+ ```ruby
40
+ Broadcast.setup do |config|
41
+ config.jabber { |jabber|
42
+ jabber.username = 'foo@foo.com'
43
+ jabber.password = 'mypass'
44
+ jabber.recipients = 'mike@foo.com'
45
+ }
46
+ end
47
+ ```
48
+
49
+ Now to send a message, you need to define Message class, like this:
50
+
51
+ ```ruby
52
+ class Poke < Broadcast::Message
53
+ medium :jabber
54
+
55
+ def body
56
+ "Poke!"
57
+ end
58
+ end
59
+ ```
60
+
61
+ When you're ready, just instantiate the Message class and call #publish:
62
+
63
+ ```ruby
64
+ Poke.new.publish
65
+ ```
66
+
67
+ Delayed::Job
68
+ ------------
69
+
70
+ Broadcast plays nicely with Delayed::Job. For example to publish a message in a delayed job, simply change the above example to:
71
+
72
+ ```ruby
73
+ Poke.new.delay.publish
74
+ ```
75
+
76
+
77
+ Media
78
+ -----
79
+
80
+ Broadcast currently ships with support for following Media:
81
+
82
+ ### Jabber
83
+
84
+ Broadcast::Medium::Jabber is based on the xmpp4r gem.
85
+
86
+ #### Example setup
87
+
88
+ ```ruby
89
+ Broadcast.setup do |config|
90
+ config.jabber { |jabber|
91
+ jabber.username = 'myaccount@gmail.com'
92
+ jabber.password = 'mypass'
93
+ jabber.recipients = 'mike@foo.com'
94
+ }
95
+ end
96
+ ```
97
+
98
+ ### Email
99
+
100
+ Broadcast::Medium::Email is based on the mail gem.
101
+
102
+ #### Example setup
103
+
104
+ This is an example setup with smtp delivery method with Gmail
105
+
106
+ ```ruby
107
+ Broadcast.setup do |config|
108
+ config.email { |email|
109
+ email.recipients = ['foo@moo.com']
110
+ email.delivery_method = :smtp
111
+ email.delivery_options = {
112
+ :address => "smtp.gmail.com",
113
+ :port => 587,
114
+ :domain => 'your.host.name',
115
+ :user_name => '<username>',
116
+ :password => '<password>',
117
+ :authentication => 'plain',
118
+ :enable_starttls_auto => true
119
+ }
120
+ }
121
+ end
122
+ ```
123
+
124
+ ### Twitter
125
+
126
+ Broadcast::Medium::Twitter is based on the oauth gem.
127
+ In order to use it, you will need a application registered on Twitter.
128
+
129
+ When you have it, run rake broadcast:authorize:twitter, which will help you get all the required keys and tokens.
130
+
131
+ #### Example setup
132
+
133
+ ```ruby
134
+ Broadcast.setup do |config|
135
+ config.twitter { |twitter|
136
+ twitter.consumer_key = 'consumerkey'
137
+ twitter.consumer_secret = 'consumersecret'
138
+ twitter.access_token = 'accesstoken'
139
+ twitter.access_secret = 'accesssecret'
140
+ }
141
+ end
142
+ ```
143
+
144
+ ### Yammer
145
+
146
+ Broadcast::Medium::Yammer is based on the oauth gem.
147
+ In order to use it, you will need a application registered on Yammer.
148
+
149
+ When you have it, run rake broadcast:authorize:yammer, which will help you get all the required keys and tokens.
150
+
151
+ #### Example setup
152
+
153
+ ```ruby
154
+ Broadcast.setup do |config|
155
+ config.twitter { |yammer|
156
+ yammer.consumer_key = 'consumerkey'
157
+ yammer.consumer_secret = 'consumersecret'
158
+ yammer.access_token = 'accesstoken'
159
+ yammer.access_secret = 'accesssecret'
160
+ }
161
+ end
162
+ ```
163
+
164
+ ### Log
165
+
166
+ Broadcast::Medium::Log is a simple writer to a log file
167
+
168
+ #### Example setup
169
+
170
+ ```ruby
171
+ Broadcast.setup do |config|
172
+ config.log.file = 'log/broadcast.log'
173
+ end
174
+ ```
175
+
176
+ ### Campfire
177
+
178
+ Broadcast::Medium::Campfire is based on the broach gem.
179
+
180
+ #### Example setup
181
+
182
+ ```ruby
183
+ Broadcast.setup do |config|
184
+ config.campfire { |campfire|
185
+ campfire.subdomain = 'myaccount'
186
+ campfire.token = 'token'
187
+ campfire.room = 'My Room'
188
+ }
189
+ end
190
+ ```
191
+
192
+ ### Irc
193
+
194
+ Broadcast::Medium::Irc employs the shout-bot gem.
195
+
196
+ #### Example setup
197
+
198
+ ```ruby
199
+ Broadcast.setup do |config|
200
+ config.irc { |irc|
201
+ irc.username = 'myusername',
202
+ irc.server = 'irc.freenode.net',
203
+ irc.port = '6667',
204
+ irc.channel = 'mychannel',
205
+ }
206
+ end
207
+ ```
208
+
209
+ Copyright
210
+ ---------
211
+
212
+ Copyright (c) 2011 Marcin Bunsch, Future Simple Inc. See LICENSE for details.
data/Rakefile ADDED
@@ -0,0 +1,35 @@
1
+ require 'rubygems'
2
+ require 'bundler/setup'
3
+
4
+ require 'bundler'
5
+
6
+ Bundler::GemHelper.install_tasks
7
+
8
+ begin
9
+
10
+ require 'rspec'
11
+ require "rspec/core/rake_task"
12
+
13
+ desc "Run all examples"
14
+ RSpec::Core::RakeTask.new(:spec) do |t|
15
+ t.rspec_path = 'bin/rspec'
16
+ t.rspec_opts = %w[--color]
17
+ t.verbose = false
18
+ end
19
+
20
+ namespace :spec do
21
+ task :cleanup do
22
+ rm_rf 'coverage.data'
23
+ end
24
+
25
+ RSpec::Core::RakeTask.new :rcov do |t|
26
+ t.rcov = true
27
+ t.rcov_opts = %[-Ilib -Ispec --exclude "gems/*,features"]
28
+ t.verbose = false
29
+ end
30
+
31
+ end
32
+
33
+ rescue
34
+ puts 'Could not load Broadcast RSpec Rake tasks'
35
+ end
data/broadcast.gemspec ADDED
@@ -0,0 +1,28 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "broadcast/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "broadcast"
7
+ s.version = Broadcast::VERSION
8
+ s.platform = Gem::Platform::RUBY
9
+ s.authors = ["Marcin Bunsch"]
10
+ s.email = ["marcin@futuresimple.com"]
11
+ s.homepage = "http://github.com/futuresimple/broadcast"
12
+ s.summary = %q{A broadcasting microframework making publishing of messages to different services easy}
13
+ s.description = %q{A broadcasting microframework making publishing of messages to different services easy}
14
+
15
+ s.add_dependency 'hashie'
16
+
17
+ # 'Externalable' dependencies
18
+ s.add_dependency 'oauth'
19
+ s.add_dependency 'xmpp4r'
20
+ s.add_dependency 'mail'
21
+ s.add_dependency 'broach'
22
+ s.add_dependency 'shout-bot'
23
+
24
+ s.files = `git ls-files`.split("\n")
25
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
26
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
27
+ s.require_paths = ["lib"]
28
+ end
@@ -0,0 +1,20 @@
1
+ class Broadcast::Config
2
+
3
+ # Allow usage of namespaces in config
4
+ def method_missing(meth, *args, &block)
5
+ @namespaces ||= {}
6
+ stringified = meth.to_s
7
+ if stringified[-1].chr == '=' and args.first
8
+ key = stringified[0..-2].to_sym
9
+ @namespaces[key] = args.first
10
+ elsif block
11
+ key = stringified[0..-1].to_sym
12
+ @namespaces[key] ||= Hashie::Mash.new
13
+ block.call(@namespaces[key])
14
+ else
15
+ key = stringified[0..-1].to_sym
16
+ @namespaces[key] ||= Hashie::Mash.new
17
+ end
18
+ end
19
+
20
+ end
@@ -0,0 +1,10 @@
1
+ require 'broach'
2
+
3
+ class Broadcast::Medium::Campfire < Broadcast::Medium::Oauth
4
+
5
+ def publish(message)
6
+ Broach.settings = { 'account' => options.subdomain, 'token' => options.token, 'use_ssl' => true }
7
+ Broach.speak(options.room, message.body)
8
+ end
9
+
10
+ end
@@ -0,0 +1,22 @@
1
+ require 'mail'
2
+
3
+ class Broadcast::Medium::Email < Broadcast::Medium::Oauth
4
+
5
+ def publish(message)
6
+ recipients = options.recipients.is_a?(Array) ? options.recipients : [options.recipients]
7
+ options = self.options
8
+ recipients.compact.each do |recipient|
9
+ mail = Mail.new do
10
+ from options.sender || message.class.to_s
11
+ to recipient
12
+ subject message.subject || message.class.to_s
13
+ body message.body
14
+ end
15
+ if options.delivery_method
16
+ mail.delivery_method options.delivery_method, options.delivery_options || {}
17
+ end
18
+ mail.deliver
19
+ end
20
+ end
21
+
22
+ end
@@ -0,0 +1,12 @@
1
+ require 'shout-bot'
2
+
3
+ class Broadcast::Medium::Irc < Broadcast::Medium::Oauth
4
+ def publish(message)
5
+ uri = "irc://#{options.username}"
6
+
7
+ uri += "@#{options.server}:#{options.port ? options.port : '6667'}"
8
+ uri += "/##{options.channel.to_s.gsub("#","") }"
9
+
10
+ ShoutBot.shout(uri) { |room| room.say message }
11
+ end
12
+ end
@@ -0,0 +1,33 @@
1
+ require 'xmpp4r'
2
+
3
+ class Broadcast::Medium::Jabber < Broadcast::Medium::Oauth
4
+
5
+ class Client
6
+
7
+ attr_accessor :client
8
+
9
+ def initialize(username, password, server = nil, port = 5222)
10
+ @jid = Jabber::JID::new(username)
11
+ @client = Jabber::Client::new(@jid)
12
+ client.connect(server, port)
13
+ client.auth(password)
14
+ end
15
+
16
+ def deliver(recipient, body)
17
+ client.send Jabber::Message::new(recipient, body)
18
+ end
19
+
20
+ end
21
+
22
+ def jabber
23
+ @jabber ||= Client.new(options.username, options.password, options.server)
24
+ end
25
+
26
+ def publish(message)
27
+ recipients = options.recipients.is_a?(Array) ? options.recipients : [options.recipients]
28
+ recipients.compact.each do |recipient|
29
+ jabber.deliver(recipient, message.body)
30
+ end
31
+ end
32
+
33
+ end
@@ -0,0 +1,7 @@
1
+ class Broadcast::Medium::Log < Broadcast::Medium
2
+
3
+ def publish(message)
4
+ Logger.new(options.file).info(message.body)
5
+ end
6
+
7
+ end
@@ -0,0 +1,43 @@
1
+ require 'oauth'
2
+
3
+ class Broadcast::Medium::Oauth < Broadcast::Medium
4
+
5
+ class << self
6
+ attr_accessor :site
7
+ end
8
+
9
+ def consumer
10
+ @consumer ||= OAuth::Consumer.new(options.consumer_key, options.consumer_secret, :site => self.class.site)
11
+ end
12
+
13
+ def token
14
+ @access_token ||= OAuth::AccessToken.new(consumer, options.access_token, options.access_secret)
15
+ end
16
+
17
+ def authorize
18
+ unless options.consumer_key
19
+ print "Enter consumer key: "
20
+ options.consumer_key = $stdin.gets.chomp
21
+ end
22
+ unless options.consumer_secret
23
+ print "Enter consumer secret: "
24
+ options.consumer_secret = $stdin.gets.chomp
25
+ end
26
+ request_token = consumer.get_request_token
27
+ puts "\nGo to this url and click 'Authorize' to get the token:"
28
+ puts request_token.authorize_url
29
+ print "\nEnter token: "
30
+ token = $stdin.gets.chomp
31
+
32
+ access_token = request_token.get_access_token(:oauth_verifier => token)
33
+
34
+ puts "\nAuthorization complete! Put the following in your Broadcast configuration file:\n\n"
35
+ puts "Broadcast.setup do |config|\n\n"
36
+ puts " config.#{namespace}.consumer_key = '#{consumer.key}'"
37
+ puts " config.#{namespace}.consumer_secret = '#{consumer.key}'"
38
+ puts " config.#{namespace}.access_token = '#{access_token.token}'"
39
+ puts " config.#{namespace}.access_secret = '#{access_token.secret}'"
40
+ puts "\nend"
41
+ end
42
+
43
+ end
@@ -0,0 +1,9 @@
1
+ class Broadcast::Medium::Twitter < Broadcast::Medium::Oauth
2
+
3
+ self.site = "http://api.twitter.com"
4
+
5
+ def publish(message)
6
+ token.post '/1/statuses/update.json', { :status => message.body }
7
+ end
8
+
9
+ end
@@ -0,0 +1,9 @@
1
+ class Broadcast::Medium::Yammer < Broadcast::Medium::Oauth
2
+
3
+ self.site = "https://www.yammer.com"
4
+
5
+ def publish(message)
6
+ token.post '/api/v1/messages.json', { :body => message.body }
7
+ end
8
+
9
+ end
@@ -0,0 +1,27 @@
1
+ class Broadcast::Medium
2
+
3
+ # Set up autoload for the media
4
+ Dir.glob(File.join(Broadcast::ROOT, 'broadcast', 'media', '*')).each do |file|
5
+ name = File.basename(file).split('.').first
6
+ autoload name.capitalize.to_sym, file
7
+ end
8
+
9
+ def initialize(options = {})
10
+ # load in the configuration from Broadcast setup
11
+ @options = Broadcast.configuration.send(namespace) || Hashie::Mash.new
12
+ # Override the configuration using the supplied options
13
+ @options = @options.merge(options)
14
+ end
15
+
16
+ def namespace
17
+ @namespace ||= self.class.name.split('::').last.downcase.to_sym
18
+ end
19
+
20
+ def options
21
+ @options
22
+ end
23
+
24
+ def publish(message)
25
+ end
26
+
27
+ end
@@ -0,0 +1,33 @@
1
+ class Broadcast::Message
2
+
3
+ attr_accessor :options
4
+ class << self
5
+ attr_accessor :media
6
+ def medium(name, options = {})
7
+ self.media ||= []
8
+ self.media.push({ :name => name, :options => options })
9
+ end
10
+ end
11
+
12
+ def initialize(options = {})
13
+ @options = Hashie::Mash.new(options)
14
+ end
15
+
16
+ def publish
17
+ (self.class.media || []).each do |medium|
18
+ begin
19
+ Broadcast::Medium.const_get(medium[:name].to_s.downcase.capitalize).new(medium[:options]).publish(self)
20
+ rescue
21
+ Broadcast.logger.error "Publishing of #{self.class.name} to #{medium[:name]} failed:\n#{$!}"
22
+ end
23
+ end
24
+ end
25
+
26
+ def subject
27
+ end
28
+
29
+ def body
30
+ ""
31
+ end
32
+
33
+ end
@@ -0,0 +1,11 @@
1
+ require 'rails/railtie'
2
+
3
+ class Broadcast
4
+
5
+ class Railtie < Rails::Railtie
6
+ rake_tasks do
7
+ load 'broadcast/tasks/broadcast.rake'
8
+ end
9
+ end
10
+
11
+ end
@@ -0,0 +1,19 @@
1
+ namespace "broadcast" do
2
+
3
+ namespace "authorize" do
4
+
5
+ desc "Authorize Broadcast with Yammer and get access token information"
6
+ task "yammer" do
7
+ require 'broadcast'
8
+ Broadcast::Medium::Yammer.new.authorize
9
+ end
10
+
11
+ desc "Authorize Broadcast with Twitter and get access token information"
12
+ task "twitter" do
13
+ require 'broadcast'
14
+ Broadcast::Medium::Twitter.new.authorize
15
+ end
16
+
17
+ end
18
+
19
+ end
@@ -0,0 +1,5 @@
1
+ class Broadcast
2
+
3
+ VERSION = "0.1.0"
4
+
5
+ end
data/lib/broadcast.rb ADDED
@@ -0,0 +1,34 @@
1
+ require 'logger'
2
+ require 'hashie'
3
+
4
+ class Broadcast
5
+
6
+ ROOT = File.dirname(__FILE__)
7
+
8
+ class << self
9
+ attr_accessor :logger
10
+ attr_accessor :configuration
11
+ end
12
+
13
+ # Basic configuration object
14
+ require 'broadcast/config'
15
+ self.configuration = Broadcast::Config.new
16
+
17
+ # Default
18
+ self.logger = Logger.new($stdout)
19
+
20
+ def self.publish(type, options = {})
21
+ name = type.to_s.strip.split("_").collect(&:capitalize).join('')
22
+ message = Broadcast::Message.const_get(name).new
23
+ message.publish
24
+ message
25
+ end
26
+
27
+ def self.setup(&block)
28
+ block.call(self.configuration)
29
+ end
30
+
31
+ end
32
+
33
+ require 'broadcast/medium'
34
+ require 'broadcast/message'
@@ -0,0 +1,19 @@
1
+ require 'spec_helper'
2
+
3
+ describe Broadcast::Medium::Campfire do
4
+
5
+ describe '#publish' do
6
+
7
+ before do
8
+ @medium = Broadcast::Medium::Campfire.new
9
+ @message = Broadcast::Message::SpecWithContent.new
10
+ end
11
+
12
+ it "should send a post request to the campfire API with the message body" do
13
+ Broach.should_receive(:speak).with("My Room", 'message')
14
+ @medium.publish(@message)
15
+ end
16
+
17
+ end
18
+
19
+ end