moxie-columbus 0.1.2.1

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/Gemfile ADDED
@@ -0,0 +1,5 @@
1
+ source 'https://gems.gemfury.com/qsNMqt3pW3Ar1QaNqsdX/'
2
+ source "http://rubygems.org"
3
+
4
+ # Specify your gem's dependencies in firehose.gemspec
5
+ gemspec
data/Gemfile.lock ADDED
@@ -0,0 +1,86 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ moxie-columbus (0.1.2.1)
5
+ moxie-columbus
6
+
7
+ GEM
8
+ remote: https://gems.gemfury.com/qsNMqt3pW3Ar1QaNqsdX/
9
+ remote: http://rubygems.org/
10
+ specs:
11
+ activesupport (4.0.2)
12
+ i18n (~> 0.6, >= 0.6.4)
13
+ minitest (~> 4.2)
14
+ multi_json (~> 1.3)
15
+ thread_safe (~> 0.1)
16
+ tzinfo (~> 0.3.37)
17
+ addressable (2.3.5)
18
+ atomic (1.1.14)
19
+ builder (3.2.2)
20
+ descendants_tracker (0.0.3)
21
+ fakeweb (1.3.0)
22
+ faraday (0.8.8)
23
+ multipart-post (~> 1.2.0)
24
+ git (1.2.6)
25
+ github_api (0.11.1)
26
+ addressable (~> 2.3)
27
+ descendants_tracker (~> 0.0.1)
28
+ faraday (~> 0.8, < 0.10)
29
+ hashie (>= 1.2)
30
+ multi_json (>= 1.7.5, < 2.0)
31
+ nokogiri (~> 1.6.0)
32
+ oauth2
33
+ hashie (2.0.5)
34
+ highline (1.6.20)
35
+ httpauth (0.2.0)
36
+ i18n (0.6.9)
37
+ jeweler (2.0.0)
38
+ builder
39
+ bundler (>= 1.0)
40
+ git (>= 1.2.5)
41
+ github_api
42
+ highline (>= 1.6.15)
43
+ nokogiri (>= 1.5.10)
44
+ rake
45
+ rdoc
46
+ jnunemaker-matchy (0.4.0)
47
+ json (1.8.1)
48
+ jwt (0.1.8)
49
+ multi_json (>= 1.5)
50
+ mini_portile (0.5.2)
51
+ minitest (4.7.5)
52
+ multi_json (1.8.2)
53
+ multi_xml (0.5.5)
54
+ multipart-post (1.2.0)
55
+ nokogiri (1.6.1)
56
+ mini_portile (~> 0.5.0)
57
+ oauth2 (0.9.2)
58
+ faraday (~> 0.8)
59
+ httpauth (~> 0.2)
60
+ jwt (~> 0.1.4)
61
+ multi_json (~> 1.0)
62
+ multi_xml (~> 0.5)
63
+ rack (~> 1.2)
64
+ rack (1.5.2)
65
+ rake (10.1.1)
66
+ rdoc (4.1.1)
67
+ json (~> 1.4)
68
+ shoulda (3.5.0)
69
+ shoulda-context (~> 1.0, >= 1.0.1)
70
+ shoulda-matchers (>= 1.4.1, < 3.0)
71
+ shoulda-context (1.1.6)
72
+ shoulda-matchers (2.4.0)
73
+ activesupport (>= 3.0.0)
74
+ thread_safe (0.1.3)
75
+ atomic
76
+ tzinfo (0.3.38)
77
+
78
+ PLATFORMS
79
+ ruby
80
+
81
+ DEPENDENCIES
82
+ fakeweb
83
+ jeweler
84
+ jnunemaker-matchy
85
+ moxie-columbus!
86
+ shoulda
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2009 John Nunemaker
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.rdoc ADDED
@@ -0,0 +1,18 @@
1
+ = columbus
2
+
3
+ Autodiscovers feeds from urls.
4
+
5
+ = examples
6
+
7
+ See examples directory. Short example below.
8
+
9
+ require 'pp'
10
+ pp Columbus.new('http://www.railstips.org').primary
11
+
12
+ #<struct Columbus::Feed
13
+ url="http://feeds2.feedburner.com/railstips",
14
+ title="Railstips Articles">
15
+
16
+ == Copyright
17
+
18
+ Copyright (c) 2009 John Nunemaker. See LICENSE for details.
data/Rakefile ADDED
@@ -0,0 +1,49 @@
1
+ require 'rubygems'
2
+ require 'rake'
3
+
4
+ begin
5
+ require 'jeweler'
6
+ Jeweler::Tasks.new do |gem|
7
+ gem.name = "moxie-columbus"
8
+ gem.summary = %Q{Autodiscovers feeds from urls}
9
+ gem.email = "nunemaker@gmail.com"
10
+ gem.homepage = "http://github.com/jnunemaker/columbus"
11
+ gem.authors = ["John Nunemaker"]
12
+ gem.rubyforge_project = 'columbus'
13
+ # gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
14
+ end
15
+ rescue LoadError
16
+ puts "Jeweler not available. Install it with: sudo gem install technicalpickles-jeweler -s http://gems.github.com"
17
+ end
18
+
19
+ # require 'rake/task'
20
+ # Rake::Task.new do |rdoc|
21
+ # rdoc.rdoc_dir = 'rdoc'
22
+ # rdoc.title = 'columbus'
23
+ # rdoc.options << '--line-numbers' << '--inline-source'
24
+ # rdoc.rdoc_files.include('README*')
25
+ # rdoc.rdoc_files.include('lib/**/*.rb')
26
+ # end
27
+
28
+ require 'rake/testtask'
29
+ Rake::TestTask.new(:test) do |test|
30
+ test.libs << 'lib' << 'test'
31
+ test.pattern = 'test/**/*_test.rb'
32
+ test.verbose = false
33
+ end
34
+
35
+ begin
36
+ require 'rcov/rcovtask'
37
+ Rcov::RcovTask.new do |test|
38
+ test.libs << 'test'
39
+ test.pattern = 'test/**/*_test.rb'
40
+ test.verbose = true
41
+ end
42
+ rescue LoadError
43
+ task :rcov do
44
+ abort "RCov is not available. In order to run rcov, you must: sudo gem install spicycode-rcov"
45
+ end
46
+ end
47
+
48
+
49
+ task :default => :test
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.1.2.1
data/examples/all.rb ADDED
@@ -0,0 +1,4 @@
1
+ require File.dirname(__FILE__) + '/../lib/columbus'
2
+ require 'pp'
3
+
4
+ pp Columbus.new('http://www.railstips.org').all
@@ -0,0 +1,4 @@
1
+ require File.dirname(__FILE__) + '/../lib/columbus'
2
+ require 'pp'
3
+
4
+ pp Columbus.new('http://www.railstips.org').primary
data/lib/columbus.rb ADDED
@@ -0,0 +1,47 @@
1
+ require 'logger'
2
+ require 'net/http'
3
+ require 'uri'
4
+ require 'rubygems'
5
+ require 'nokogiri'
6
+
7
+ require File.dirname(__FILE__) + '/columbus/feed'
8
+ require File.dirname(__FILE__) + '/columbus/link'
9
+ require File.dirname(__FILE__) + '/columbus/redirect_follower'
10
+
11
+ class Columbus
12
+ attr_reader :url
13
+
14
+ def initialize(url)
15
+ @url = url
16
+ end
17
+
18
+ def primary
19
+ @primary ||= begin
20
+ response = RedirectFollower.new(url).resolve
21
+ @url = response.url
22
+ if hpricot_link = parse_links(response.body)[0]
23
+ link_to_feed(hpricot_link)
24
+ end
25
+ end
26
+ end
27
+
28
+ def all
29
+ @all ||= begin
30
+ response = RedirectFollower.new(url).resolve
31
+ @url = response.url
32
+ parse_links(response.body).map { |hpricot_link| link_to_feed(hpricot_link) }
33
+ end
34
+ end
35
+
36
+ private
37
+ def link_to_feed(element)
38
+ link = Link.new(url, element.attributes['href'].to_s, element.attributes['title'].to_s)
39
+ Feed.new(link.absolute_url, link.clean_title)
40
+ end
41
+
42
+ def parse_links(html)
43
+ Nokogiri::HTML(html).search('link').select do |link|
44
+ link.attributes['type'].to_s =~ /application\/(rss|atom)\+xml/i
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,17 @@
1
+ class Columbus
2
+ class Feed < Struct.new(:url, :title)
3
+ attr_accessor :body
4
+
5
+ def initialize(*args)
6
+ super(*args)
7
+ determine_feed_endpoint
8
+ end
9
+
10
+ def determine_feed_endpoint
11
+ response = RedirectFollower.new(url).resolve
12
+ self.url = response.url
13
+ self.body = response.body
14
+ self
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,19 @@
1
+ class Columbus
2
+ class Link < Struct.new(:url, :href, :title)
3
+ def clean_title
4
+ title.nil? ? nil : title.strip
5
+ end
6
+
7
+ def absolute_url
8
+ if relative?
9
+ "#{url}#{href}"
10
+ else
11
+ "#{href}"
12
+ end
13
+ end
14
+
15
+ def relative?
16
+ href =~ /^\//
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,46 @@
1
+ class Columbus
2
+ class RedirectFollower
3
+ class TooManyRedirects < StandardError; end
4
+
5
+ attr_accessor :url, :body, :redirect_limit, :response
6
+
7
+ def initialize(url, options={})
8
+ @url = url
9
+ @redirect_limit = options.delete(:limit) || 5
10
+ logger.level = options.delete(:level) || Logger::WARN
11
+ end
12
+
13
+ def logger
14
+ @logger ||= Logger.new(STDOUT)
15
+ end
16
+
17
+ def resolve
18
+ raise TooManyRedirects if redirect_limit < 0
19
+
20
+ self.response = Net::HTTP.get_response(URI.parse(url))
21
+
22
+ logger.info "redirect limit: #{redirect_limit}"
23
+ logger.info "response code: #{response.code}"
24
+ logger.debug "response body: #{response.body}"
25
+
26
+ if response.kind_of?(Net::HTTPRedirection)
27
+ self.url = redirect_url
28
+ self.redirect_limit -= 1
29
+
30
+ logger.info "redirect found, headed to #{url}"
31
+ resolve
32
+ end
33
+
34
+ self.body = response.body
35
+ self
36
+ end
37
+
38
+ def redirect_url
39
+ if response['location'].nil?
40
+ response.body.match(/<a href=\"([^>]+)\">/i)[1]
41
+ else
42
+ response['location']
43
+ end
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,71 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE DIRECTLY
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
4
+ # -*- encoding: utf-8 -*-
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = "moxie-columbus"
8
+ s.version = "0.1.2.1"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["John Nunemaker"]
12
+ s.date = "2014-01-09"
13
+ s.email = "nunemaker@gmail.com"
14
+ s.extra_rdoc_files = [
15
+ "LICENSE",
16
+ "README.rdoc"
17
+ ]
18
+ s.files = [
19
+ "Gemfile",
20
+ "Gemfile.lock",
21
+ "LICENSE",
22
+ "README.rdoc",
23
+ "Rakefile",
24
+ "VERSION",
25
+ "examples/all.rb",
26
+ "examples/primary.rb",
27
+ "lib/columbus.rb",
28
+ "lib/columbus/feed.rb",
29
+ "lib/columbus/link.rb",
30
+ "lib/columbus/redirect_follower.rb",
31
+ "moxie-columbus.gemspec",
32
+ "test/columbus_test.rb",
33
+ "test/feed_test.rb",
34
+ "test/fixtures/railsquicktips.html",
35
+ "test/fixtures/railstips.html",
36
+ "test/fixtures/railstips_feedburner.html",
37
+ "test/fixtures/railstips_redirect",
38
+ "test/link_test.rb",
39
+ "test/test_helper.rb"
40
+ ]
41
+ s.homepage = "http://github.com/jnunemaker/columbus"
42
+ s.require_paths = ["lib"]
43
+ s.rubyforge_project = "columbus"
44
+ s.rubygems_version = "1.8.25"
45
+ s.summary = "Autodiscovers feeds from urls"
46
+
47
+ if s.respond_to? :specification_version then
48
+ s.specification_version = 3
49
+
50
+ if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
51
+ s.add_runtime_dependency(%q<moxie-columbus>, [">= 0"])
52
+ s.add_development_dependency(%q<shoulda>, [">= 0"])
53
+ s.add_development_dependency(%q<jeweler>, [">= 0"])
54
+ s.add_development_dependency(%q<fakeweb>, [">= 0"])
55
+ s.add_development_dependency(%q<jnunemaker-matchy>, [">= 0"])
56
+ else
57
+ s.add_dependency(%q<moxie-columbus>, [">= 0"])
58
+ s.add_dependency(%q<shoulda>, [">= 0"])
59
+ s.add_dependency(%q<jeweler>, [">= 0"])
60
+ s.add_dependency(%q<fakeweb>, [">= 0"])
61
+ s.add_dependency(%q<jnunemaker-matchy>, [">= 0"])
62
+ end
63
+ else
64
+ s.add_dependency(%q<moxie-columbus>, [">= 0"])
65
+ s.add_dependency(%q<shoulda>, [">= 0"])
66
+ s.add_dependency(%q<jeweler>, [">= 0"])
67
+ s.add_dependency(%q<fakeweb>, [">= 0"])
68
+ s.add_dependency(%q<jnunemaker-matchy>, [">= 0"])
69
+ end
70
+ end
71
+
@@ -0,0 +1,36 @@
1
+ $:.unshift '.';require File.dirname(__FILE__) + '/test_helper'
2
+
3
+ class ColumbusTest < Test::Unit::TestCase
4
+ context "Initialization" do
5
+ should "accept a url" do
6
+ Columbus.new('http://foobar.com').url.should == 'http://foobar.com'
7
+ end
8
+ end
9
+
10
+ should "be able to get primary feed" do
11
+ FakeWeb.register_uri(:get, "http://railstips.org/", :string => fixture_file('railstips.html'))
12
+ FakeWeb.register_uri(:get, "http://feeds.feedburner.com/railstips", :string => fixture_file('railstips_feedburner.html'))
13
+ expected = Columbus::Feed.new('http://feeds.feedburner.com/railstips', 'Railstips Articles')
14
+ Columbus.new('http://railstips.org').primary.should == expected
15
+ end
16
+
17
+ should "be able to get all feeds" do
18
+ FakeWeb.register_uri(:get, "http://railstips.org/", :string => fixture_file('railstips.html'))
19
+ FakeWeb.register_uri(:get, "http://feeds.feedburner.com/railstips", :string => fixture_file('railstips_feedburner.html'))
20
+ FakeWeb.register_uri(:get, "http://feeds.feedburner.com/railsquicktips", :string => fixture_file('railsquicktips.html'))
21
+ Columbus.new('http://railstips.org').all.should == [
22
+ Columbus::Feed.new('http://feeds.feedburner.com/railstips', 'Railstips Articles'),
23
+ Columbus::Feed.new('http://feeds.feedburner.com/railsquicktips', 'Rails Quick Tips (links)')
24
+ ]
25
+ end
26
+
27
+ should "follow redirects" do
28
+ FakeWeb.register_uri(:get, "http://railstips.org/", :string => fixture_file('railstips.html'))
29
+ FakeWeb.register_uri(:get, "http://feeds.feedburner.com/railstips", :response => fixture_file('railstips_redirect'))
30
+ FakeWeb.register_uri(:get, "http://feeds2.feedburner.com/railstips", :string => fixture_file('railstips_feedburner.html'))
31
+ primary = Columbus.new('http://railstips.org').primary
32
+ primary.url.should == 'http://feeds2.feedburner.com/railstips'
33
+ primary.title.should == 'Railstips Articles'
34
+ end
35
+
36
+ end