broadcast 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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