lifestream 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/README ADDED
File without changes
data/Rakefile ADDED
@@ -0,0 +1,77 @@
1
+ require 'rubygems'
2
+ require 'rake'
3
+
4
+ require(File.join(File.dirname(__FILE__), 'lib', 'lifestream'))
5
+
6
+ begin
7
+ require 'jeweler'
8
+ Jeweler::Tasks.new do |gem|
9
+ gem.name = "lifestream"
10
+ gem.summary = %Q{downloads and parses lifestream information from sources like facebook and twitter}
11
+ gem.email = "adamhunter@me.com"
12
+ gem.homepage = "http://github.com/adamhunter/lifestream"
13
+ gem.authors = ["Adam Hunter"]
14
+ #gem.rubyforge_project = "lifestream"
15
+ gem.files = FileList["[A-Z]*", "{lib,test}/**/*"]
16
+
17
+ #gem.add_dependency('httparty', '0.4.3')
18
+
19
+ gem.add_development_dependency('thoughtbot-shoulda', '>= 2.10.1')
20
+ gem.add_development_dependency('jnunemaker-matchy', '0.4.0')
21
+ gem.add_development_dependency('mocha', '0.9.4')
22
+ gem.add_development_dependency('redgreen', '1.0.0')
23
+ end
24
+
25
+ Jeweler::RubyforgeTasks.new do |rubyforge|
26
+ rubyforge.doc_task = "rdoc"
27
+ end
28
+ rescue LoadError
29
+ puts "Jeweler not available. Install it with: sudo gem install technicalpickles-jeweler -s http://gems.github.com"
30
+ end
31
+
32
+ require 'rake/testtask'
33
+ Rake::TestTask.new(:test) do |test|
34
+ test.libs << 'lib' << 'test'
35
+ test.pattern = 'test/**/*_test.rb'
36
+ test.verbose = false
37
+ end
38
+
39
+ begin
40
+ require 'rcov/rcovtask'
41
+ Rcov::RcovTask.new do |test|
42
+ test.libs << 'test'
43
+ test.pattern = 'test/**/*_test.rb'
44
+ test.verbose = true
45
+ end
46
+ rescue LoadError
47
+ task :rcov do
48
+ abort "RCov is not available. In order to run rcov, you must: sudo gem install spicycode-rcov"
49
+ end
50
+ end
51
+
52
+
53
+ task :default => :test
54
+
55
+ require 'rake/rdoctask'
56
+ Rake::RDocTask.new do |rdoc|
57
+ if File.exist?('VERSION.yml')
58
+ config = YAML.load(File.read('VERSION.yml'))
59
+ version = "#{config[:major]}.#{config[:minor]}.#{config[:patch]}"
60
+ else
61
+ version = ""
62
+ end
63
+
64
+ rdoc.rdoc_dir = 'rdoc'
65
+ rdoc.title = "lifestream #{version}"
66
+ rdoc.rdoc_files.include('README*')
67
+ rdoc.rdoc_files.include('lib/**/*.rb')
68
+ end
69
+
70
+ namespace :lifestream do
71
+
72
+ desc "Generate lifestream.yml file"
73
+ task :configure do
74
+
75
+ end
76
+
77
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.1.0
data/lib/lifestream.rb ADDED
@@ -0,0 +1,55 @@
1
+ require 'rubygems'
2
+ require 'erb'
3
+
4
+ module Lifestream
5
+
6
+ # Provides configurability to Lifestream. There are a number of options available, such as:
7
+ # * whiny: Will raise an error if Lifestream cannot process a given feed.
8
+ # Defaults to true
9
+ # * config: Path to the lifestream.yml file that contains the feeds to download.
10
+ # Defaults to gem location, you will probably want to set this
11
+ def self.options
12
+ @options ||= {
13
+ :config => File.join(File.expand_path(File.dirname(__FILE__)), '..', 'lifestream.yml'),
14
+ :whiny => true
15
+ }
16
+ end
17
+
18
+ # Generates a Lifestream::Stream which contains all the channels
19
+ # with their branches parsed from the feeds in the lifestream.yml file,
20
+ # ordered by their published_at date.
21
+ def self.run(preload = false)
22
+ lifestream = Lifestream::Stream.new
23
+ ensure_configuration_exists
24
+
25
+ feeds = YAML.load(File.open(options[:config]))
26
+ feeds.each_pair do |name, options|
27
+ klass = Lifestream::Channel.const_get(options['format'])
28
+ channel = klass.new(name, options['url'])
29
+ channel.branches if preload
30
+ lifestream.channels << channel
31
+ end
32
+ lifestream
33
+ end
34
+
35
+ protected
36
+
37
+ def self.ensure_configuration_exists
38
+ raise Lifestream::NoConfiguration.new("You must provide a configuration file") unless File.exists?(options[:config])
39
+ end
40
+
41
+ class NoConfiguration < StandardError; end
42
+
43
+ end
44
+
45
+ directory = File.expand_path(File.dirname(__FILE__))
46
+
47
+ # yes most of these are all named after parts of a stream
48
+ # http://en.wikipedia.org/wiki/Stream#Parts_of_a_stream
49
+ require File.join(directory, 'lifestream', 'branch')
50
+ require File.join(directory, 'lifestream', 'channel')
51
+ require File.join(directory, 'lifestream', 'channel', 'rss2')
52
+ require File.join(directory, 'lifestream', 'request')
53
+ require File.join(directory, 'lifestream', 'source')
54
+ require File.join(directory, 'lifestream', 'stream')
55
+
@@ -0,0 +1,11 @@
1
+ module Lifestream
2
+ class Branch
3
+
4
+ attr_accessor :published_at, :title, :body
5
+
6
+ def initialize published_at, title, body = nil
7
+ @published_at, @title, @body = published_at, title, body
8
+ end
9
+
10
+ end
11
+ end
@@ -0,0 +1,55 @@
1
+ module Lifestream
2
+ class Channel
3
+
4
+ attr_accessor :name, :branches, :raw_text, :feed
5
+
6
+ def initialize name, url
7
+ @name, @branches, @request = name, [], Lifestream::Request.new(url)
8
+ end
9
+
10
+ def branches
11
+ create_branches if @branches.empty?
12
+ @branches
13
+ end
14
+
15
+ protected
16
+
17
+ def download
18
+ response = @request.get
19
+ raise unless response.kind_of?(Net::HTTPSuccess)
20
+ @raw_data = response.body
21
+ rescue => e
22
+ Lifestream::Channel::DownloadError.new("The URL #{@request.url} failed to download") if Lifestream.options[:whiny]
23
+ end
24
+
25
+ def create_branches
26
+ download
27
+ parse
28
+ feed_to_a.each do |item|
29
+ @branches << to_branch(item)
30
+ end
31
+ end
32
+
33
+ def parse
34
+ raise_method_error :parse
35
+ end
36
+
37
+ def feed_to_a
38
+ raise_method_error :feed_to_a
39
+ end
40
+
41
+ def to_branch
42
+ raise_method_error :to_branch
43
+ end
44
+
45
+ private
46
+
47
+ def raise_method_error method
48
+ raise Lifestream::Channel::MethodError, "Method `#{method}` must be overriden in the child class"
49
+ end
50
+
51
+ class MethodError < StandardError; end
52
+ class DownloadError < StandardError; end
53
+
54
+ end
55
+ end
@@ -0,0 +1,26 @@
1
+ require 'rss/1.0'
2
+ require 'rss/2.0'
3
+
4
+ module Lifestream
5
+ class Channel::Rss2 < Lifestream::Channel
6
+
7
+ protected
8
+
9
+ def parse
10
+ @feed = RSS::Parser.parse(@raw_data, false)
11
+ rescue => e
12
+ raise Lifestream::Channel::Rss2::MalformedFeed.new("The feed from #{@url} could not be parsed") if Lifestream.options[:whiny]
13
+ end
14
+
15
+ def feed_to_a
16
+ @feed.items || []
17
+ end
18
+
19
+ def to_branch(branch)
20
+ Lifestream::Branch.new(branch.pubDate, branch.title, branch.description)
21
+ end
22
+
23
+ class MalformedFeed < StandardError; end
24
+
25
+ end
26
+ end
@@ -0,0 +1,42 @@
1
+ module Lifestream
2
+ class Request
3
+
4
+ attr_accessor :url, :max_redirects
5
+
6
+ def initialize(url, options = {})
7
+ @url = url
8
+ options[:max_redirects] ||= 5
9
+ @max_redirects = options[:max_redirects]
10
+ end
11
+
12
+ def get(url = @url)
13
+ check_max_redirects
14
+ url, request = build_request(url)
15
+ response = Net::HTTP.start(url.host, url.port) do |http|
16
+ http.request(request)
17
+ end
18
+ if response.kind_of?(Net::HTTPRedirection)
19
+ @redirect += 1
20
+ get(response.header['location'])
21
+ else
22
+ response
23
+ end
24
+ end
25
+
26
+ protected
27
+
28
+ def build_request(url)
29
+ url = URI.parse(url)
30
+ request = Net::HTTP::Get.new("#{url.path}?#{url.query}", {'User-Agent' => 'Gecko'})
31
+ return url, request
32
+ end
33
+
34
+ def check_max_redirects
35
+ @redirect ||= 0
36
+ raise TooManyRedirects if @redirect > @max_redirects
37
+ end
38
+
39
+ class TooManyRedirects < StandardError; end
40
+
41
+ end
42
+ end
@@ -0,0 +1,13 @@
1
+ module Lifestream
2
+ class Source
3
+
4
+ # TODO: Implement this
5
+ def self.configure
6
+ yaml = ERB.new <<-EOF
7
+
8
+ EOF
9
+ yaml.result
10
+ end
11
+
12
+ end
13
+ end
@@ -0,0 +1,20 @@
1
+ module Lifestream
2
+ class Stream
3
+
4
+ attr_accessor :channels
5
+
6
+ def initialize channels = []
7
+ @channels = channels
8
+ end
9
+
10
+ def branches
11
+ @branches ||= []
12
+ return @branches unless @branches.empty?
13
+ @channels.each do |channel|
14
+ channel.branches.each { |b| @branches << b }
15
+ end
16
+ @branches.sort { |a,b| a.published_at <=> b.published_at }.reverse
17
+ end
18
+
19
+ end
20
+ end
@@ -0,0 +1,9 @@
1
+ facebook :
2
+ url : http://www.facebook.com/feeds/status.php?id=778750315&viewer=778750315&key=110b5987c2&format=rss20
3
+ format : Rss2
4
+ pandora :
5
+ url : http://feeds.pandora.com/feeds/people/ah125i/favorites.xml
6
+ format : Rss2
7
+ twitter :
8
+ url : http://twitter.com/statuses/user_timeline/15344521.rss
9
+ format : Rss2
File without changes
File without changes
File without changes
@@ -0,0 +1,16 @@
1
+ require 'test_helper'
2
+
3
+ class StreamTest < Test::Unit::TestCase
4
+
5
+ context "A Lifestream::Stream instance" do
6
+
7
+ setup { @lifestream = Lifestream.run }
8
+
9
+ should "return its branches in descending chronological order" do
10
+ @lifestream.branches.should_not be_nil
11
+ @lifestream.branches[0].published_at.should > @lifestream.branches[1].published_at
12
+ end
13
+
14
+ end
15
+
16
+ end
@@ -0,0 +1,28 @@
1
+ require 'test_helper'
2
+
3
+ class LifestreamTest < Test::Unit::TestCase
4
+
5
+ context "The Lifestream module" do
6
+
7
+ should "create a Lifestream::Stream instance with all of its channels and branches" do
8
+ @lifestream = Lifestream::run
9
+ @lifestream.channels.size.should == 3
10
+ @lifestream.channels.each { |c| c.should_not be_nil }
11
+ @lifestream.channels.each { |c| c.should be_a(Lifestream::Channel::Rss2) }
12
+ @lifestream.channels.each do |c|
13
+ c.branches.each { |b| b.should_not be_nil }
14
+ c.branches.each { |b| b.should be_a(Lifestream::Branch) }
15
+ end
16
+ end
17
+
18
+ should "set the configuration file to the provided path" do
19
+ original_path = Lifestream.options[:config]
20
+ test_config_path = 'some/config'
21
+ Lifestream.options[:config] = test_config_path
22
+ Lifestream.options[:config].should == test_config_path
23
+ Lifestream.options[:config] = original_path
24
+ end
25
+
26
+ end
27
+
28
+ end
@@ -0,0 +1,39 @@
1
+ require 'test/unit'
2
+ require 'rubygems'
3
+
4
+ gem 'thoughtbot-shoulda', '>= 2.10.1'
5
+ gem 'jnunemaker-matchy', '0.4.0'
6
+ gem 'mocha', '0.9.7'
7
+ gem 'redgreen', '>= 1.0.0'
8
+
9
+ require 'shoulda'
10
+ require 'matchy'
11
+ require 'mocha'
12
+ require 'fakeweb'
13
+ require 'redgreen' unless ENV['TM_FILENAME']
14
+
15
+ directory = File.expand_path(File.dirname(__FILE__))
16
+
17
+ require File.join(directory, '..', 'lib', 'lifestream')
18
+
19
+ Lifestream.options[:config] = 'lifestream.yml'
20
+
21
+ class Test::Unit::TestCase
22
+
23
+ custom_matcher :be_nil do |receiver, matcher, args|
24
+ matcher.positive_failure_message = "Expected #{receiver} to be nil but it wasn't"
25
+ matcher.negative_failure_message = "Expected #{receiver} not to be nil but it was"
26
+ receiver.nil?
27
+ end
28
+
29
+ custom_matcher :be_a do |receiver, matcher, args|
30
+ receiver.class == args[0]
31
+ end
32
+
33
+ custom_matcher :be_empty do |receiver, matcher, args|
34
+ matcher.positive_failure_message = "Expected #{receiver} to be empty but it wasn't"
35
+ matcher.negative_failure_message = "Expected #{receiver} not to be empty but it was"
36
+ receiver.empty?
37
+ end
38
+
39
+ end
metadata ADDED
@@ -0,0 +1,115 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: lifestream
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Adam Hunter
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2009-10-03 00:00:00 -04:00
13
+ default_executable:
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: thoughtbot-shoulda
17
+ type: :development
18
+ version_requirement:
19
+ version_requirements: !ruby/object:Gem::Requirement
20
+ requirements:
21
+ - - ">="
22
+ - !ruby/object:Gem::Version
23
+ version: 2.10.1
24
+ version:
25
+ - !ruby/object:Gem::Dependency
26
+ name: jnunemaker-matchy
27
+ type: :development
28
+ version_requirement:
29
+ version_requirements: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "="
32
+ - !ruby/object:Gem::Version
33
+ version: 0.4.0
34
+ version:
35
+ - !ruby/object:Gem::Dependency
36
+ name: mocha
37
+ type: :development
38
+ version_requirement:
39
+ version_requirements: !ruby/object:Gem::Requirement
40
+ requirements:
41
+ - - "="
42
+ - !ruby/object:Gem::Version
43
+ version: 0.9.4
44
+ version:
45
+ - !ruby/object:Gem::Dependency
46
+ name: redgreen
47
+ type: :development
48
+ version_requirement:
49
+ version_requirements: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - "="
52
+ - !ruby/object:Gem::Version
53
+ version: 1.0.0
54
+ version:
55
+ description:
56
+ email: adamhunter@me.com
57
+ executables: []
58
+
59
+ extensions: []
60
+
61
+ extra_rdoc_files:
62
+ - README
63
+ files:
64
+ - README
65
+ - Rakefile
66
+ - VERSION
67
+ - lib/lifestream.rb
68
+ - lib/lifestream/branch.rb
69
+ - lib/lifestream/channel.rb
70
+ - lib/lifestream/channel/rss2.rb
71
+ - lib/lifestream/request.rb
72
+ - lib/lifestream/source.rb
73
+ - lib/lifestream/stream.rb
74
+ - test/lifestream.yml
75
+ - test/lifestream/branch_test.rb
76
+ - test/lifestream/channel_test.rb
77
+ - test/lifestream/source_test.rb
78
+ - test/lifestream/stream_test.rb
79
+ - test/lifestream_test.rb
80
+ - test/test_helper.rb
81
+ has_rdoc: true
82
+ homepage: http://github.com/adamhunter/lifestream
83
+ licenses: []
84
+
85
+ post_install_message:
86
+ rdoc_options:
87
+ - --charset=UTF-8
88
+ require_paths:
89
+ - lib
90
+ required_ruby_version: !ruby/object:Gem::Requirement
91
+ requirements:
92
+ - - ">="
93
+ - !ruby/object:Gem::Version
94
+ version: "0"
95
+ version:
96
+ required_rubygems_version: !ruby/object:Gem::Requirement
97
+ requirements:
98
+ - - ">="
99
+ - !ruby/object:Gem::Version
100
+ version: "0"
101
+ version:
102
+ requirements: []
103
+
104
+ rubyforge_project:
105
+ rubygems_version: 1.3.5
106
+ signing_key:
107
+ specification_version: 3
108
+ summary: downloads and parses lifestream information from sources like facebook and twitter
109
+ test_files:
110
+ - test/lifestream/branch_test.rb
111
+ - test/lifestream/channel_test.rb
112
+ - test/lifestream/source_test.rb
113
+ - test/lifestream/stream_test.rb
114
+ - test/lifestream_test.rb
115
+ - test/test_helper.rb