stream_grabber 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README.md +67 -0
- data/Rakefile +11 -0
- data/app/helpers/stream_grabber_helper.rb +14 -0
- data/lib/stream_grabber/github.rb +24 -0
- data/lib/stream_grabber/last_fm.rb +28 -0
- data/lib/stream_grabber/twitter.rb +25 -0
- data/lib/stream_grabber.rb +42 -0
- data/test/dummy_responses/github.atom +2108 -0
- data/test/dummy_responses/lastfm.xml +712 -0
- data/test/dummy_responses/twitter.json +255 -0
- data/test/github_test.rb +26 -0
- data/test/lastfm_test.rb +19 -0
- data/test/stream_grabber_test.rb +39 -0
- data/test/test_helper.rb +32 -0
- data/test/twitter_test.rb +26 -0
- metadata +176 -0
data/README.md
ADDED
@@ -0,0 +1,67 @@
|
|
1
|
+
StreamGrabber
|
2
|
+
=============
|
3
|
+
|
4
|
+
StreamGrabber is a way of multiplexing streams and feeds together into a timeline, it supports twitter, last.fm and github at the moment but is designed to be fairly trivial to add new sources, providing they have some kind of public facing data stream…
|
5
|
+
|
6
|
+
Usage
|
7
|
+
-----
|
8
|
+
At the moment StreamGrabber supports Twitter, last.fm and Github so
|
9
|
+
you'll need to give it your username for these sites. Create
|
10
|
+
`#{RAILS_ROOT}/config/stream_grabber.yml` so that it looks like the
|
11
|
+
following
|
12
|
+
|
13
|
+
:twitter: username
|
14
|
+
:lastfm: username
|
15
|
+
:github: username
|
16
|
+
|
17
|
+
And then you can include the plugin in your Gemfile like so
|
18
|
+
|
19
|
+
gem 'stream_grabber'
|
20
|
+
|
21
|
+
and load the helper method in your chosen controller.
|
22
|
+
|
23
|
+
class HomepageController < ApplicationController
|
24
|
+
helper :stream_grabber
|
25
|
+
|
26
|
+
# blah blah blah
|
27
|
+
end
|
28
|
+
|
29
|
+
then in your views you use
|
30
|
+
|
31
|
+
<%= generate_stream_list %>
|
32
|
+
|
33
|
+
to render an ordered list of your most recent activity, most recent
|
34
|
+
first. There are a couple of additional options you can pass to
|
35
|
+
customise the output, the first is the number of results you'd like back
|
36
|
+
(defaults to 15) and the second is the css id and class you wish to be
|
37
|
+
applied to the output. You can use these as follows
|
38
|
+
|
39
|
+
<%= generate_stream_list 5, :class => 'foo' , :id => 'bar' %>
|
40
|
+
|
41
|
+
Adding new Sources
|
42
|
+
------------------
|
43
|
+
|
44
|
+
Sources are defined as classes in the StreamGrabber module, they are all
|
45
|
+
loaded dynamically so all your new source has to do is conform to the
|
46
|
+
following interface
|
47
|
+
|
48
|
+
module StreamGrabber
|
49
|
+
class Twitter
|
50
|
+
def initialize(user_name)
|
51
|
+
# do your setup in here to grab and parse the stream
|
52
|
+
end
|
53
|
+
|
54
|
+
def last_five
|
55
|
+
# returns your data as a timestamp keyed hash
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
And as long as `last_five` returns a hash with timestamps as keys, then
|
61
|
+
whatever is in the hash values will be squirted into the `li` elements
|
62
|
+
in the resulting list.
|
63
|
+
|
64
|
+
Over engineered? Damn Straight!!
|
65
|
+
|
66
|
+
If you want to add sources, please give me a pull request and I'll merge
|
67
|
+
them straight in.
|
data/Rakefile
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
module StreamGrabberHelper
|
2
|
+
def generate_stream_list(num=15, *args)
|
3
|
+
if args.present?
|
4
|
+
css_class = args.first[:class]
|
5
|
+
css_id = args.first[:id]
|
6
|
+
end
|
7
|
+
|
8
|
+
data = StreamGrabber.grab(num).inject([]){ |memo,acc| memo << content_tag(:li,raw(acc[1])) }
|
9
|
+
|
10
|
+
content_tag :ol, {:class => css_class || 'activity_list', :id => css_id || 'stream_grabber'} do
|
11
|
+
raw(data.join("\n"))
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
require 'simple-rss'
|
2
|
+
require 'open-uri'
|
3
|
+
require 'pp'
|
4
|
+
|
5
|
+
module StreamGrabber
|
6
|
+
class Github
|
7
|
+
def initialize(user_name)
|
8
|
+
@doc = SimpleRSS.parse(open("https://github.com/#{user_name}.private.actor.atom?token=4ec7548d4e071bdc85462077a69bcdf2"))
|
9
|
+
end
|
10
|
+
|
11
|
+
def last_five
|
12
|
+
commits = {}
|
13
|
+
@doc.items.first(5).each do |item|
|
14
|
+
date = Time.parse(item[:updated].to_s).to_i
|
15
|
+
commits[date] = message_from(item[:link], item[:title])
|
16
|
+
end
|
17
|
+
commits
|
18
|
+
end
|
19
|
+
|
20
|
+
def message_from(link, title)
|
21
|
+
%Q{<a href="#{link}">#{title}</a>"}
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
require 'nokogiri'
|
2
|
+
require 'open-uri'
|
3
|
+
|
4
|
+
module StreamGrabber
|
5
|
+
class LastFm
|
6
|
+
def initialize(user_name)
|
7
|
+
@user_name = user_name
|
8
|
+
@doc ||= Nokogiri::XML(open("http://ws.audioscrobbler.com/2.0/?method=user.getlovedtracks&user=#{user_name}&api_key=b25b959554ed76058ac220b7b2e0a026"))
|
9
|
+
end
|
10
|
+
|
11
|
+
def last_five
|
12
|
+
tracks = {}
|
13
|
+
ff = @doc.xpath("//track").first(5)
|
14
|
+
ff.each do |elem|
|
15
|
+
date_loved = elem.>('date').first['uts'].to_i
|
16
|
+
artist_name = elem.css('artist > name').text
|
17
|
+
track_name = elem.>('name').text
|
18
|
+
url = elem.>('url').text
|
19
|
+
tracks[date_loved] = message_for(artist_name,track_name, url)
|
20
|
+
end
|
21
|
+
tracks
|
22
|
+
end
|
23
|
+
|
24
|
+
def message_for(artist, title, url)
|
25
|
+
%Q{ #{@user_name} loved the track <a href="#{url}">#{title} by #{artist}</a> on Last.fm }
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
require 'json'
|
2
|
+
require 'open-uri'
|
3
|
+
|
4
|
+
module StreamGrabber
|
5
|
+
class Twitter
|
6
|
+
def initialize(user_name)
|
7
|
+
@user_name = user_name
|
8
|
+
@doc = JSON.load(open("http://api.twitter.com/1/statuses/user_timeline.json?count=6&screen_name=#{user_name}"))
|
9
|
+
end
|
10
|
+
|
11
|
+
def last_five
|
12
|
+
tweets = {}
|
13
|
+
@doc.each do |tweet|
|
14
|
+
date = Time.parse(tweet["created_at"]).to_i
|
15
|
+
tweets[date] = message_from(tweet["text"], tweet["id"])
|
16
|
+
end
|
17
|
+
tweets
|
18
|
+
end
|
19
|
+
|
20
|
+
def message_from(text, id)
|
21
|
+
%Q{<a href="http://twitter.com/#{@user_name}/status/#{id}">#{text}</a>}
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
require 'stream_grabber/last_fm'
|
2
|
+
require 'stream_grabber/github'
|
3
|
+
require 'stream_grabber/twitter'
|
4
|
+
require 'rails'
|
5
|
+
|
6
|
+
if ENV['OFFLINE']
|
7
|
+
# use test data if we are developing offline
|
8
|
+
require File.expand_path(File.join(File.dirname(__FILE__), *%w[.. test test_helper.rb]))
|
9
|
+
end
|
10
|
+
|
11
|
+
module StreamGrabber
|
12
|
+
|
13
|
+
class Engine < Rails::Engine; end
|
14
|
+
|
15
|
+
class << self
|
16
|
+
def usernames
|
17
|
+
@usernames ||= YAML.load_file(Rails.root + 'config/stream_grabber.yml')
|
18
|
+
end
|
19
|
+
|
20
|
+
def mux_stream
|
21
|
+
messages = {}
|
22
|
+
StreamGrabber.constants.each do |klass|
|
23
|
+
k = StreamGrabber.const_get(klass)
|
24
|
+
if k.instance_of?(Class) and k.method_defined?('last_five')
|
25
|
+
name = k.name.partition('::').last.split(/(?=[A-Z])/).join("_").downcase
|
26
|
+
user_name = usernames[name.to_sym]
|
27
|
+
messages.merge!(k.new(user_name).send(:last_five))
|
28
|
+
end
|
29
|
+
end
|
30
|
+
arr = messages.sort.reverse
|
31
|
+
end
|
32
|
+
|
33
|
+
def grab_all
|
34
|
+
mux_stream
|
35
|
+
end
|
36
|
+
|
37
|
+
def grab(n)
|
38
|
+
mux_stream.first(n)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
end
|