jsl-feedtosis 0.0.2.3

Sign up to get free protection for your applications and to get access to all the features.
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2009 Justin S. Leitgeb
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,141 @@
1
+ = Description
2
+
3
+ Feedtosis is a library that helps you to efficiently process syndicated web
4
+ resources. It helps by automatically using conditional HTTP GET requests, as
5
+ well as by pointing out which entries are new in any given feed.
6
+
7
+ == Philosophy
8
+
9
+ Feedtosis is designed to help you with book-keeping about feed fetching
10
+ details. This is usually something that is mundane and not fundamentally related
11
+ to the business logic of applications that deal with the consumption of
12
+ syndicated content on the web. Feedtosis keeps track of these mundane
13
+ details so you can just keep grabbing new content without wasting bandwidth in
14
+ making unnecessary requests and programmer time in implementing algorithms to
15
+ figure out which feed entries are new.
16
+
17
+ Feedtosis fits into other frameworks to do the heavy lifting, including the
18
+ Curb library which does HTTP requests through curl, and FeedNormalizer which
19
+ abstracts the differences between syndication formats. In the sense that it fits
20
+ into these existing, robust programs, Feedtosis is a modular middleware
21
+ piece that efficiently glues together disparate parts to create a helpful feed
22
+ reader with a minimal (< 200 LOC), test-covered codebase.
23
+
24
+ == Installation
25
+
26
+ Assuming that you've followed the directions on gems.github.com to allow your
27
+ computer to install gems from GitHub, the following command will install the
28
+ Feedtosis library:
29
+
30
+ sudo gem install jsl-feedtosis
31
+
32
+ == Usage
33
+
34
+ Feedtosis is easy to use. Just create a client object, and invoke the
35
+ "fetch" method:
36
+
37
+ require 'feedtosis'
38
+ client = Feedtosis::Client.new('http://feeds.feedburner.com/wooster')
39
+ result = client.fetch
40
+
41
+ +result+ will be a Feedtosis::Result object which delegates methods to
42
+ the FeedNormalizer::Feed object as well as the Curl::Easy object used to fetch
43
+ the feed. Useful methods on this object include +entries+, +new_entries+ and
44
+ +response_code+ among many others (basically all of the methods that
45
+ FeedNormalizer::Feed and Curl::Easy objects respond to are implemented and can
46
+ be called directly, minus the setter methods for these objects).
47
+
48
+ Note that since Feedtosis uses HTTP conditional GET, it may not actually
49
+ have received a full XML response from the server suitable for being parsed
50
+ into entries. In this case, methods such as +entries+ on the Feedtosis::Result
51
+ will return +nil+. Depending on your application logic, you may want to inspect
52
+ the methods that are delegated to the Curl::Easy object, such as +response_code+,
53
+ for more information on what happened in these cases.
54
+
55
+ On subsequent requests of a particular resource, Feedtosis will update
56
+ +new_entries+ to contain the feed entries that we haven't seen yet. In most
57
+ applications, your program will probably call the same batch of URLS multiple
58
+ times, and process the elements in +new_entries+.
59
+
60
+ You will most likely want to allow Feedtosis to remember details about the
61
+ last retrieval of a feed after the client is removed from memory. Feedtosis
62
+ uses Moneta, a unified interface to key-value storage systems to remember
63
+ "summaries" of feeds that it has seen in the past. See the document section on
64
+ Customization for more details on how to configure this system.
65
+
66
+ == Customization
67
+
68
+ Feedtosis stores summaries of feeds in a key-value storage system. If no
69
+ options are included when creating a new Feedtosis::Client object, the
70
+ default is to use a "memory" storage system. The memory system is just a basic
71
+ ruby Hash, so it won't keep track of feeds after a particular Client is removed
72
+ from memory. To configure a different backend, pass an options hash to the
73
+ Feedtosis client initialization:
74
+
75
+ url = "http://newsrss.bbc.co.uk/rss/newsonline_world_edition/south_asia/rss.xml"
76
+ mf = Feedtosis::Client.new(url, :backend => {:moneta_klass => 'Moneta::Memcache', :server => 'localhost:1978'})
77
+ res = mf.fetch
78
+
79
+ This example sets up a Memcache backend, which in this case points to Tokyo
80
+ Tyrant on port 1978. Note that Moneta::Memcache can be given as a string, in
81
+ which case you don't have to manually require Moneta::Memcache before
82
+ initializing the client.
83
+
84
+ Generally, Feedtosis supports all systems supported by Moneta, and any one
85
+ of the supported systems can be given to the +moneta_klass+ parameter. Other
86
+ options following +backend+ are passed directly to Moneta for configuration.
87
+
88
+ == Implementation
89
+
90
+ Feedtosis helps to identify new feed entries and to figure out when
91
+ conditional GET can be used in retrieving resources. In order to accomplish this
92
+ without having to require that the user store information such as etags and
93
+ dates of the last retrieved entry, Feedtosis stores a summary structure in
94
+ the configured key-value store (backed by Moneta). In order to do conditional
95
+ GET requests, Feedtosis stores the Last-Modified date, as well as the ETag
96
+ of the last request in the summary structure, which is put in a namespaced
97
+ element consisting of the term 'Feedtosis' (bet you won't have to worry
98
+ about name collisions on that one!) and the MD5 of the URL retrieved.
99
+
100
+ It can also be a bit tricky to decipher which feed entries are new since many
101
+ feed sources don't include unique ids with their feeds. Feedtosis reliably
102
+ keeps track of which entries in a feed are new by storing (in the summary hash
103
+ mentioned above) an MD5 signature of each entry in a feed. It takes elements
104
+ such as the published-at date, title and content and generates the MD5 of these
105
+ elements. This allows Feedtosis to cheaply compute (both in terms of
106
+ computation and storage) which feed entries should be presented to the user as
107
+ "new". Below is an example of a summary structure:
108
+
109
+ {
110
+ :etag => "4c8f-46ac09fbbe940",
111
+ :last_modified => "Mon, 25 May 2009 18:17:33 GMT",
112
+ :digests => ["f2993783ded928637ce5f2dc2d837f10", "da64efa6dd9ce34e5699b9efe73a37a7"]
113
+ }
114
+
115
+ The data stored by Feedtosis in the summary structure allows it to be
116
+ helpful to the user without storing lots of data that are unnecessary for
117
+ efficient functioning.
118
+
119
+ == HTML cleaning/sanitizing
120
+
121
+ Feedtosis doesn't do anything about feed sanitizing, as other libraries have
122
+ been built for this purpose. FeedNormalizer has methods for escaping entries,
123
+ but to strip HTML I suggest that you look at the Ruby gem "sanitize".
124
+
125
+ == Credits
126
+
127
+ Thanks to Sander Hartlage (GitHub: Sander6) for useful feedback early in the
128
+ development of Feedtosis.
129
+
130
+ == Feedback
131
+
132
+ Please let me know if you have any problems with or questions about
133
+ Feedtosis.
134
+
135
+ = References
136
+
137
+ (1) http://en.wikipedia.org/wiki/List_of_vores
138
+
139
+ = Author
140
+
141
+ Justin S. Leitgeb, mailto:justin@phq.org
data/Rakefile ADDED
@@ -0,0 +1,28 @@
1
+ require 'rubygems'
2
+ require 'spec'
3
+
4
+ require 'rake'
5
+ require 'spec/rake/spectask'
6
+ require 'rake/rdoctask'
7
+
8
+ require 'lib/feedtosis'
9
+
10
+ desc 'Test the plugin.'
11
+ Spec::Rake::SpecTask.new(:spec) do |t|
12
+ t.spec_opts = ["--format", "progress", "--colour"]
13
+ t.libs << 'lib'
14
+ t.verbose = true
15
+ end
16
+
17
+ desc "Run all the tests"
18
+ task :default => :spec
19
+
20
+ desc 'Generate documentation'
21
+ Rake::RDocTask.new(:rdoc) do |rdoc|
22
+ rdoc.rdoc_dir = 'rdoc'
23
+ rdoc.title = 'Feedtosis'
24
+ rdoc.options << '--line-numbers' << '--inline-source'
25
+ rdoc.rdoc_files.include('README.rdoc')
26
+ rdoc.rdoc_files.include('lib/feedtosis/**/*.rb')
27
+ end
28
+
data/feedtosis.gemspec ADDED
@@ -0,0 +1,49 @@
1
+ Gem::Specification.new do |s|
2
+ s.name = %q{feedtosis}
3
+ s.version = "0.0.2.3"
4
+
5
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
6
+ s.authors = ["Justin Leitgeb"]
7
+ s.date = %q{2009-05-22}
8
+ s.description = %q{Feedtosis finds new information in feeds quickly using smart fetching and matching of previously read entries}
9
+ s.email = %q{justin@phq.org}
10
+
11
+ s.files = ["lib/extensions/core/array.rb", "lib/extensions/core/hash.rb",
12
+ "lib/extensions/feed_normalizer/feed_instance_methods.rb",
13
+ "lib/feedtosis/result.rb",
14
+ "lib/feedtosis/client.rb", "lib/feedtosis.rb", "LICENSE",
15
+ "feedtosis.gemspec", "Rakefile", "README.rdoc",
16
+ "spec/extensions/feed_normalizer/feed_instance_methods_spec.rb",
17
+ "spec/fixtures/http_headers/wooster.txt",
18
+ "spec/fixtures/xml/older_wooster.xml", "spec/fixtures/xml/wooster.xml",
19
+ "spec/feedtosis/client_spec.rb", "spec/feedtosis_spec.rb",
20
+ "spec/feedtosis/result_spec.rb",
21
+ "spec/spec_helper.rb"]
22
+
23
+ s.has_rdoc = true
24
+ s.homepage = %q{http://github.com/jsl/feedtosis}
25
+ s.rdoc_options = ["--charset=UTF-8"]
26
+ s.require_paths = ["lib"]
27
+ s.rubygems_version = %q{1.3.1}
28
+ s.summary = %q{Retrieves feeds using conditional GET and marks entries that you haven't seen before}
29
+ s.test_files = ["spec/feedtosis_spec.rb", "spec/spec_helper.rb", "spec/feedtosis/client_spec.rb",
30
+ "spec/feedtosis/result_spec.rb" ]
31
+
32
+ s.extra_rdoc_files = [ "README.rdoc" ]
33
+
34
+ s.rdoc_options += [
35
+ '--title', 'Feedtosis',
36
+ '--main', 'README.rdoc',
37
+ '--line-numbers',
38
+ '--inline-source'
39
+ ]
40
+
41
+ %w[ taf2-curb jsl-moneta jsl-http_headers namelessjon-feed_me ].each do |dep|
42
+ s.add_dependency(dep)
43
+ end
44
+
45
+ if s.respond_to? :specification_version then
46
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
47
+ s.specification_version = 2
48
+ end
49
+ end
@@ -0,0 +1,8 @@
1
+ class Array
2
+
3
+ # From Rails' ActiveSupport library
4
+ def extract_options!
5
+ last.is_a?(::Hash) ? pop : {}
6
+ end
7
+
8
+ end
@@ -0,0 +1,17 @@
1
+ class Hash
2
+ # Returns a Hash containing only input keys.
3
+ # Method from merb-core.
4
+ def except(*rejected)
5
+ reject { |k,v| rejected.include?(k) }
6
+ end
7
+
8
+ def reverse_merge(other_hash)
9
+ other_hash.merge(self)
10
+ end
11
+
12
+ # Returns a new hash containing only the input keys.
13
+ # Method from merb-core.
14
+ def only(*allowed)
15
+ reject { |k,v| !allowed.include?(k) }
16
+ end
17
+ end
@@ -0,0 +1,17 @@
1
+ # Extends FeedNormalizer::Feed with method for detecting new_items (aliased as new_entries for
2
+ # convenience).
3
+ module Feedtosis
4
+ module FeedInstanceMethods
5
+
6
+ # Returns only the feeds that are new.
7
+ def new_items
8
+ self.entries.select do |e|
9
+ e.instance_variable_get(:@_seen) == false
10
+ end
11
+ end
12
+
13
+ alias :new_entries :new_items
14
+ end
15
+ end
16
+
17
+ FeedNormalizer::Feed.__send__(:include, Feedtosis::FeedInstanceMethods)
@@ -0,0 +1,146 @@
1
+ module Feedtosis
2
+
3
+ # Feedtosis::Client is the primary interface to the feed reader. Call it
4
+ # with a url that was previously fetched while connected to the configured
5
+ # backend, and it will 1) only do a retrieval if deemed necessary based on the
6
+ # etag and modified-at of the last etag and 2) mark all entries retrieved as
7
+ # either new or not new. Entries retrieved are normalized using the
8
+ # feed-normalizer gem.
9
+ class Client
10
+ attr_reader :options, :url
11
+
12
+ # Initializes a new feedtosis library. The backend can be a hash of options, in
13
+ # which case we initialize a new HashBack::Backend. Or, it may be a pre-initialized
14
+ # backend, in which case we set the backend to the given HashBack::Backend object.
15
+ def initialize(url, backend = Moneta::Memory.new)
16
+ @url = url
17
+ @backend = backend
18
+
19
+ unless @backend.respond_to?(:[]) && @backend.respond_to?(:[]=)
20
+ raise ArgumentError, "Backend needs to be a key-value store"
21
+ end
22
+ end
23
+
24
+ # Retrieves the latest entries from this feed. Returns a Feedtosis::Result
25
+ # object which delegates methods to the Curl::Easy object making the request
26
+ # and the FeedNormalizer::Feed object that may have been created from the
27
+ # HTTP response body.
28
+ def fetch
29
+ curl = build_curl_easy
30
+ curl.perform
31
+ feed = process_curl_response(curl)
32
+ Feedtosis::Result.new(curl, feed)
33
+ end
34
+
35
+ private
36
+
37
+ # Marks entries as either seen or not seen based on the unique signature of
38
+ # the entry, which is calculated by taking the MD5 of common attributes.
39
+ def mark_new_entries(response)
40
+ digests = if summary_for_feed.nil? || summary_for_feed[:digests].nil?
41
+ [ ]
42
+ else
43
+ summary_for_feed[:digests]
44
+ end
45
+
46
+ # For each entry in the responses object, mark @_seen as false if the
47
+ # digest of this entry doesn't exist in the cached object.
48
+ response.entries.each do |e|
49
+ seen = digests.include?(digest_for(e))
50
+ e.instance_variable_set(:@_seen, seen)
51
+ end
52
+
53
+ response
54
+ end
55
+
56
+ # Processes the results by identifying which entries are new if the response
57
+ # is a 200. Otherwise, returns the Curl::Easy object for the user to inspect.
58
+ def process_curl_response(curl)
59
+ if curl.response_code == 200
60
+ response = parser_for_xml(curl.body_str)
61
+ response = mark_new_entries(response)
62
+ store_summary_to_backend(response, curl)
63
+ response
64
+ end
65
+ end
66
+
67
+ # Sets options for the Curl::Easy object, including parameters for HTTP
68
+ # conditional GET.
69
+ def build_curl_easy
70
+ curl = new_curl_easy(url)
71
+
72
+ # Many feeds have a 302 redirect to another URL. For more recent versions
73
+ # of Curl, we need to specify this.
74
+ curl.follow_location = true
75
+
76
+ set_header_options(curl)
77
+ end
78
+
79
+ def new_curl_easy(url)
80
+ Curl::Easy.new(url)
81
+ end
82
+
83
+ # Returns the summary hash for this feed from the backend store.
84
+ def summary_for_feed
85
+ @backend[key_for_cached]
86
+ end
87
+
88
+ # Sets the headers from the backend, if available
89
+ def set_header_options(curl)
90
+ summary = summary_for_feed
91
+
92
+ unless summary.nil?
93
+ # We should only try to populate the headers for a conditional GET if
94
+ # we know both of these values.
95
+ if summary[:etag] && summary[:last_modified]
96
+ curl.headers['If-None-Match'] = summary[:etag]
97
+ curl.headers['If-Modified-Since'] = summary[:last_modified]
98
+ end
99
+ end
100
+
101
+ curl
102
+ end
103
+
104
+ def key_for_cached
105
+ MD5.hexdigest(@url)
106
+ end
107
+
108
+ # Stores information about the retrieval, including ETag, Last-Modified,
109
+ # and MD5 digests of all entries to the backend store. This enables
110
+ # conditional GET usage on subsequent requests and marking of entries as
111
+ # either new or seen.
112
+ def store_summary_to_backend(feed, curl)
113
+ headers = HttpHeaders.new(curl.header_str)
114
+
115
+ # Store info about HTTP retrieval
116
+ summary = { }
117
+
118
+ summary.merge!(:etag => headers.etag) unless headers.etag.nil?
119
+ summary.merge!(:last_modified => headers.last_modified) unless headers.last_modified.nil?
120
+
121
+ # Store digest for each feed entry so we can detect new feeds on the next
122
+ # retrieval
123
+ digests = feed.entries.map do |e|
124
+ digest_for(e)
125
+ end
126
+
127
+ summary.merge!(:digests => digests)
128
+ set_summary(summary)
129
+ end
130
+
131
+ def set_summary(summary)
132
+ @backend[key_for_cached] = summary
133
+ end
134
+
135
+ # Computes a unique signature for the FeedNormalizer::Entry object given.
136
+ # This signature will be the MD5 of enough fields to have a reasonable
137
+ # probability of determining if the entry is unique or not.
138
+ def digest_for(entry)
139
+ MD5.hexdigest( [ entry.title, entry.content ].join )
140
+ end
141
+
142
+ def parser_for_xml(xml)
143
+ FeedNormalizer::FeedNormalizer.parse(xml)
144
+ end
145
+ end
146
+ end
@@ -0,0 +1,48 @@
1
+ module Feedtosis
2
+
3
+ # Makes the response components both from the Curl::Easy object and the
4
+ # FeedNormalizer::Feed object available to the user by delegating appropriate
5
+ # method calls to the correct object. If FeedNormalizer wasn't able to process
6
+ # the response, calls which would be delegated to this object return nil. In
7
+ # these cases, depending on your business logic you may want to inspect the
8
+ # state of the Curl::Easy object by using methods forwarded to it.
9
+ class Result
10
+
11
+ # Methods which should be delegated to the FeedNormalizer::Feed object.
12
+ FEED_METHODS = [ :title, :description, :last_updated, :copyright, :authors,
13
+ :author, :urls, :url, :image, :generator, :items, :entries, :new_items,
14
+ :new_entries, :channel, :ttl, :skip_hours, :skip_days
15
+ ] unless defined?(FEED_METHODS)
16
+
17
+ # Precompiled regexp for detecting removing setter methods from collection
18
+ # of methods to be delegated to the Curl::Easy object.
19
+ SETTER_METHOD_RE = /=$/o unless defined?(SETTER_METHOD_RE)
20
+
21
+ def initialize(curl, feed)
22
+ @curl = curl
23
+ @feed = feed
24
+
25
+ raise ArgumentError, "Curl object must not be nil" if curl.nil?
26
+ end
27
+
28
+ # See what the Curl::Easy object responds to, and send any appropriate
29
+ # messages its way. We don't worry about setter methods, since those
30
+ # aren't really useful to delegate.
31
+ Curl::Easy.instance_methods(false).reject do |m|
32
+ m =~ SETTER_METHOD_RE
33
+ end.each do |meth| # Example method:
34
+ define_method meth do |*args| # def title
35
+ @curl.__send__(meth, *args) # @curl.title
36
+ end # end
37
+ end
38
+
39
+ # Send methods through to the feed object unless it is nil. If feed
40
+ # object is nil, return nil in response to method call.
41
+ FEED_METHODS.each do |meth| # Example method:
42
+ define_method meth do |*args| # def author
43
+ @feed.nil? ? nil : @feed.__send__(meth, *args) # @feed.nil? nil : @feed.author
44
+ end # end
45
+ end
46
+
47
+ end
48
+ end
data/lib/feedtosis.rb ADDED
@@ -0,0 +1,17 @@
1
+ require 'rubygems'
2
+ require 'curb'
3
+ require 'moneta'
4
+ require 'moneta/memory'
5
+ require 'http_headers'
6
+ require 'feed-normalizer'
7
+ require 'md5'
8
+
9
+ lib_dirs = [ 'extensions', 'feedtosis' ].map do |d|
10
+ File.join(File.dirname(__FILE__), d)
11
+ end
12
+
13
+ lib_dirs.each do |d|
14
+ Dir[File.join(d, "**", "*.rb")].each do |file|
15
+ require file
16
+ end
17
+ end
@@ -0,0 +1,12 @@
1
+ require File.join(File.dirname(__FILE__), %w[.. .. spec_helper])
2
+
3
+ describe Feedtosis::FeedInstanceMethods do
4
+ before do
5
+ @fn = FeedNormalizer::FeedNormalizer.parse(xml_fixture('wooster'))
6
+ end
7
+
8
+ it "should respond to new_entries and new_items" do
9
+ @fn.should respond_to(:new_entries)
10
+ @fn.should respond_to(:new_items)
11
+ end
12
+ end
@@ -0,0 +1,110 @@
1
+ require File.join(File.dirname(__FILE__), %w[.. spec_helper])
2
+
3
+ describe Feedtosis::Client do
4
+ before do
5
+ @url = "http://www.example.com/feed.rss"
6
+ @backend = Moneta::Memory.new
7
+ @fr = Feedtosis::Client.new(@url, @backend)
8
+ end
9
+
10
+ describe "initialization" do
11
+ it "should set #url to an Array when an Array is given" do
12
+ @fr.url.should == @url
13
+ end
14
+
15
+ it "should set the If-None-Match and If-Modified-Since headers to the value of the summary hash" do
16
+ curl_headers = mock('headers')
17
+ curl_headers.expects(:[]=).with('If-None-Match', '42ab')
18
+ curl_headers.expects(:[]=).with('If-Modified-Since', 'Mon, 25 May 2009 16:38:49 GMT')
19
+
20
+ summary = { :etag => '42ab', :last_modified => 'Mon, 25 May 2009 16:38:49 GMT' }
21
+
22
+ @fr.__send__(:set_summary, summary)
23
+
24
+ curl_easy = mock('curl', :perform => true, :follow_location= => true,
25
+ :response_code => 200, :body_str => xml_fixture('wooster'),
26
+ :header_str => http_header('wooster')
27
+ )
28
+
29
+ curl_easy.expects(:headers).returns(curl_headers).times(2)
30
+
31
+ @fr.expects(:new_curl_easy).returns(curl_easy)
32
+ @fr.fetch
33
+ end
34
+
35
+ describe "when given a pre-initialized backend" do
36
+ it "should set the @backend to the pre-initialized structure" do
37
+ h = Moneta::Memory.new
38
+ fc = Feedtosis::Client.new(@url, h)
39
+ fc.__send__(:instance_variable_get, :@backend).should == h
40
+ end
41
+
42
+ it "should raise an error if the backend is not a key-value store based on behavior" do
43
+ o = Object.new
44
+
45
+ lambda {
46
+ Feedtosis::Client.new(@url, o)
47
+ }.should raise_error(ArgumentError)
48
+ end
49
+ end
50
+ end
51
+
52
+ describe "#fetch" do
53
+ it "should call Curl::Easy.perform with the url, and #process_curl_response" do
54
+ curl_easy = mock('curl', :perform => true)
55
+ @fr.expects(:build_curl_easy).returns(curl_easy)
56
+ @fr.expects(:process_curl_response)
57
+ @fr.fetch
58
+ end
59
+
60
+ describe "when the response code is not 200" do
61
+ it "should return nil for feed methods such as #title and #author" do
62
+ curl = mock('curl', :perform => true, :response_code => 304)
63
+ @fr.expects(:build_curl_easy).returns(curl)
64
+ res = @fr.fetch
65
+ res.title.should be_nil
66
+ res.author.should be_nil
67
+ end
68
+
69
+ it "should return a Feedtosis::Result object" do
70
+ curl = mock('curl', :perform => true, :response_code => 304)
71
+ @fr.expects(:build_curl_easy).returns(curl)
72
+ @fr.fetch.should be_a(Feedtosis::Result)
73
+ end
74
+ end
75
+
76
+ describe "when the response code is 200" do
77
+ describe "when an identical resource has been retrieved previously" do
78
+ before do
79
+ curl = mock('curl', :perform => true, :response_code => 200,
80
+ :body_str => xml_fixture('wooster'), :header_str => http_header('wooster'))
81
+ @fr.expects(:build_curl_easy).returns(curl)
82
+ @fr.fetch
83
+ end
84
+
85
+ it "should have an empty array for new_entries" do
86
+ curl = mock('curl', :perform => true, :response_code => 200,
87
+ :body_str => xml_fixture('wooster'), :header_str => http_header('wooster'))
88
+ @fr.expects(:build_curl_easy).returns(curl)
89
+ @fr.fetch.new_entries.should == []
90
+ end
91
+ end
92
+
93
+ describe "when the resource has been previously retrieved minus two entries" do
94
+ before do
95
+ curl = mock('curl', :perform => true, :response_code => 200,
96
+ :body_str => xml_fixture('older_wooster'), :header_str => http_header('wooster'))
97
+ @fr.expects(:build_curl_easy).returns(curl)
98
+ @fr.fetch
99
+ end
100
+
101
+ it "should have two elements in new_entries" do
102
+ curl = mock('curl', :perform => true, :response_code => 200,
103
+ :body_str => xml_fixture('wooster'), :header_str => http_header('wooster'))
104
+ @fr.expects(:build_curl_easy).returns(curl)
105
+ @fr.fetch.new_entries.size.should == 2
106
+ end
107
+ end
108
+ end
109
+ end
110
+ end
@@ -0,0 +1,34 @@
1
+ require File.join(File.dirname(__FILE__), %w[.. spec_helper])
2
+
3
+ describe Feedtosis::Result do
4
+ before do
5
+ @c = mock('curl')
6
+ @f = mock('feed')
7
+ @r = Feedtosis::Result.new(@c, @f)
8
+ end
9
+
10
+ it "should raise an ArgumentError if the Curl object is nil" do
11
+ lambda {
12
+ Feedtosis::Result.new(nil, nil)
13
+ }.should raise_error(ArgumentError)
14
+ end
15
+
16
+ it "should send author to the Feed object" do
17
+ @f.expects(:author)
18
+ @r.author
19
+ end
20
+
21
+ it "should send body_str to the curl object" do
22
+ @c.expects(:body_str)
23
+ @r.body_str
24
+ end
25
+
26
+ it "should not respond to setter methods common in the Curl::Easy class" do
27
+ @r.should_not respond_to(:encoding=)
28
+ end
29
+
30
+ it "should return nil for author if the Feed is nil" do
31
+ r = Feedtosis::Result.new(@c, nil)
32
+ r.author
33
+ end
34
+ end
@@ -0,0 +1,5 @@
1
+ require File.join(File.dirname(__FILE__), 'spec_helper')
2
+
3
+ describe Feedtosis do
4
+
5
+ end
@@ -0,0 +1,19 @@
1
+ HTTP/1.1 302 Found
2
+ Date: Mon, 25 May 2009 17:36:02 GMT
3
+ Server: Apache/2.0.52 (Red Hat)
4
+ Location: http://feeds.feedburner.com/wooster
5
+ Content-Length: 311
6
+ Connection: close
7
+ Content-Type: text/html; charset=iso-8859-1
8
+
9
+ HTTP/1.1 200 OK
10
+ Last-Modified: Mon, 25 May 2009 16:38:49 GMT
11
+ ETag: /YJUYmsYIcQeFAGy7OZQoaCQeXw
12
+ Content-Type: text/xml; charset=utf-8
13
+ Date: Mon, 25 May 2009 17:36:03 GMT
14
+ Expires: Mon, 25 May 2009 17:36:03 GMT
15
+ Cache-Control: private, max-age=0
16
+ X-Content-Type-Options: nosniff
17
+ Server: GFE/2.0
18
+ Transfer-Encoding: chunked
19
+
@@ -0,0 +1,203 @@
1
+ <?xml version="1.0" encoding="utf-8"?>
2
+ <?xml-stylesheet type="text/xsl" media="screen" href="/~d/styles/rss2full.xsl"?>
3
+ <?xml-stylesheet type="text/css" media="screen" href="http://feeds2.feedburner.com/~d/styles/itemcontent.css"?>
4
+ <rss xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0" version="2.0">
5
+ <channel>
6
+ <title>Wooster Collective</title>
7
+ <link>http://www.woostercollective.com/</link>
8
+ <description/>
9
+ <language>en</language>
10
+ <copyright>Copyright 2009</copyright>
11
+ <lastBuildDate>Mon, 25 May 2009 07:48:04 -0500</lastBuildDate>
12
+ <generator>http://www.sixapart.com/movabletype/?v=3.3</generator>
13
+ <docs>http://blogs.law.harvard.edu/tech/rss</docs>
14
+ <atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="self" href="www.woostercollective.com/rss/index.xml" type="application/rss+xml"/>
15
+ <feedburner:emailServiceId>wooster</feedburner:emailServiceId>
16
+ <feedburner:feedburnerHostname>http://feedburner.google.com</feedburner:feedburnerHostname>
17
+ <item>
18
+ <title>Seen In North London: Department of Urban Censorship</title>
19
+ <description><![CDATA[<p><a href="http://www.woostercollective.com/urbancensor.jpg"><img alt="urbancensor.jpg" src="http://www.woostercollective.com/urbancensor-thumb.jpg" width="500" height="444" /></a></p>
20
+
21
+ <p>(Thanks, <a href="http://www.tupajumi.com/jonathan/">Jonathan</a>)<br />
22
+ </p>]]></description>
23
+ <link>http://feedproxy.google.com/~r/wooster/~3/syiP17t0PKo/seen_in_north_london_department_of_urban.html</link>
24
+ <guid isPermaLink="false">http://www.woostercollective.com/2009/05/seen_in_north_london_department_of_urban.html</guid>
25
+ <category>Wheatpastes</category>
26
+ <pubDate>Mon, 25 May 2009 07:39:16 -0500</pubDate>
27
+ <feedburner:origLink>http://www.woostercollective.com/2009/05/seen_in_north_london_department_of_urban.html</feedburner:origLink>
28
+ </item>
29
+ <item>
30
+ <title>Seen On The Steets Of Stockholm</title>
31
+ <description><![CDATA[<p><a href="http://www.woostercollective.com/poststock.jpg"><img alt="poststock.jpg" src="http://www.woostercollective.com/poststock-thumb.jpg" width="500" height="748" /></a></p>
32
+
33
+ <p>Artist: <a href="http://vimeo.com/post">Post</a></p>]]></description>
34
+ <link>http://feedproxy.google.com/~r/wooster/~3/611KGRn-ZQE/seen_on_the_steets_of_stockholm.html</link>
35
+ <guid isPermaLink="false">http://www.woostercollective.com/2009/05/seen_on_the_steets_of_stockholm.html</guid>
36
+ <category>Wheatpastes</category>
37
+ <pubDate>Mon, 25 May 2009 07:29:55 -0500</pubDate>
38
+ <feedburner:origLink>http://www.woostercollective.com/2009/05/seen_on_the_steets_of_stockholm.html</feedburner:origLink>
39
+ </item>
40
+ <item>
41
+ <title>Zevs' Electric Rainbows (2007)</title>
42
+ <description><![CDATA[<p>Zevs has updated <a href="http://www.gzzglz.com/">his website</a> with photos of past and recent project. We love the Electric Rainbows that he exchanged for advertisements back 2007:</p>
43
+
44
+ <p><a href="http://www.woostercollective.com/electric-rainbow-02.jpg"><img alt="electric-rainbow-02.jpg" src="http://www.woostercollective.com/electric-rainbow-02-thumb.jpg" width="500" height="335" /></a></p>
45
+
46
+ <p><br />
47
+ </p>]]></description>
48
+ <link>http://feedproxy.google.com/~r/wooster/~3/TpmlTS7y0f0/zevs_electric_rainbows_2007.html</link>
49
+ <guid isPermaLink="false">http://www.woostercollective.com/2009/05/zevs_electric_rainbows_2007.html</guid>
50
+ <category>Art</category>
51
+ <pubDate>Mon, 25 May 2009 07:19:27 -0500</pubDate>
52
+ <feedburner:origLink>http://www.woostercollective.com/2009/05/zevs_electric_rainbows_2007.html</feedburner:origLink>
53
+ </item>
54
+ <item>
55
+ <title>Zephyr - An Introduction</title>
56
+ <description><![CDATA[<p><a href="http://www.woostercollective.com/zephydith.jpg"><img alt="zephydith.jpg" src="http://www.woostercollective.com/zephydith-thumb.jpg" width="500" height="347" /></a></p>
57
+
58
+ <p><object width="320" height="265"><param name="movie" value="http://www.youtube.com/v/kqXXZcy-2JU&hl=en&fs=1&rel=0&hd=1"></param><param name="allowFullScreen" value="true"></param><param name="allowscriptaccess" value="always"></param><embed src="http://www.youtube.com/v/kqXXZcy-2JU&hl=en&fs=1&rel=0&hd=1" type="application/x-shockwave-flash" allowscriptaccess="always" allowfullscreen="true" width="320" height="265"></embed></object></p>
59
+
60
+ <p>One of the first graffiti artists to "accept" Sara and I back in 2000 when we started the Wooster Collective was Zephyr. This week <a href="http://www.upperplayground.com/">Upper Playground</a> posted a terrific interview with Zephyr filmed a few years ago for their Dithers DVD. </p>]]></description>
61
+ <link>http://feedproxy.google.com/~r/wooster/~3/32-ED1oPdYY/zephyr_in_introduction.html</link>
62
+ <guid isPermaLink="false">http://www.woostercollective.com/2009/05/zephyr_in_introduction.html</guid>
63
+ <category>Graffiti</category>
64
+ <pubDate>Sat, 23 May 2009 12:44:36 -0500</pubDate>
65
+ <feedburner:origLink>http://www.woostercollective.com/2009/05/zephyr_in_introduction.html</feedburner:origLink>
66
+ </item>
67
+ <item>
68
+ <title>The Yes Men - An Introduction</title>
69
+ <description><![CDATA[<div><object width="512" height="322"><param name="movie" value="http://d.yimg.com/static.video.yahoo.com/yep/YV_YEP.swf?ver=2.2.40" /><param name="allowFullScreen" value="true" /><param name="AllowScriptAccess" VALUE="always" /><param name="bgcolor" value="#000000" /><param name="flashVars" value="id=13527850&vid=13527850&lang=en-us&intl=us&thumbUrl=&embed=1" /><embed src="http://d.yimg.com/static.video.yahoo.com/yep/YV_YEP.swf?ver=2.2.40" type="application/x-shockwave-flash" width="512" height="322" allowFullScreen="true" AllowScriptAccess="always" bgcolor="#000000" flashVars="id=13527850&vid=13527850&lang=en-us&intl=us&thumbUrl=&embed=1" ></embed></object><br /><a href="http://video.yahoo.com/watch/13527850/13527850"></a> @ <a href="http://video.yahoo.com" >Yahoo! Video</a></div>
70
+
71
+ <p>If you're not familiar with <a href="http://theyesmen.org/">The Yes Men</a>, this video of their Poptech speech from 2006 is a fantastic introduction.</p>
72
+
73
+ <p>(Hat tip to <a href="http://www.brainpickings.org/">Brain Pickings</a>)</p>]]></description>
74
+ <link>http://feedproxy.google.com/~r/wooster/~3/0yXb4Ui8C3I/the_yes_men_an_introduction.html</link>
75
+ <guid isPermaLink="false">http://www.woostercollective.com/2009/05/the_yes_men_an_introduction.html</guid>
76
+ <category>Activism</category>
77
+ <pubDate>Fri, 22 May 2009 11:24:26 -0500</pubDate>
78
+ <feedburner:origLink>http://www.woostercollective.com/2009/05/the_yes_men_an_introduction.html</feedburner:origLink>
79
+ </item>
80
+ <item>
81
+ <title>A Great Example Of Site Specific Work: Carlsbad Sidewalk Surfer</title>
82
+ <description><![CDATA[<p><img alt="bryansurfer.jpg" src="http://www.woostercollective.com/bryansurfer.jpg" width="500" height="384" /></p>
83
+
84
+ <p><object width="400" height="307"><param name="allowfullscreen" value="true" /><param name="allowscriptaccess" value="always" /><param name="movie" value="http://vimeo.com/moogaloop.swf?clip_id=4772503&amp;server=vimeo.com&amp;show_title=1&amp;show_byline=1&amp;show_portrait=0&amp;color=&amp;fullscreen=1" /><embed src="http://vimeo.com/moogaloop.swf?clip_id=4772503&amp;server=vimeo.com&amp;show_title=1&amp;show_byline=1&amp;show_portrait=0&amp;color=&amp;fullscreen=1" type="application/x-shockwave-flash" allowfullscreen="true" allowscriptaccess="always" width="400" height="307"></embed></object><p><a href="http://vimeo.com/4772503">Carlsbad Sidewalk Surfer by bryan snyder</a> from <a href="http://vimeo.com/bryansnyder">Bryan Snyder</a> on <a href="http://vimeo.com">Vimeo</a>.</p></p>
85
+
86
+ <p>From <a href="http://www.snyderartdesign.com">Bryan</a>:</p>
87
+
88
+ <p>Carlsbad Sidewalk Surfer interacts with its environment during mid day wind gusts. Like the waves that tumble upon our shore, the visibility of this art piece depends on the weather. Between the glassy conditions of the morning and the mellow winds at dusk, the Carlsbad Sidewalk Surfer sneaks into another ride. The goal of this project is to showcase the relationship between a piece of art and its environment when placed in the streets."</p>]]></description>
89
+ <link>http://feedproxy.google.com/~r/wooster/~3/oXfLFSd86cY/a_great_example_of_site_specific_work_ca.html</link>
90
+ <guid isPermaLink="false">http://www.woostercollective.com/2009/05/a_great_example_of_site_specific_work_ca.html</guid>
91
+ <category>Environmental</category>
92
+ <pubDate>Fri, 22 May 2009 07:54:36 -0500</pubDate>
93
+ <feedburner:origLink>http://www.woostercollective.com/2009/05/a_great_example_of_site_specific_work_ca.html</feedburner:origLink>
94
+ </item>
95
+ <item>
96
+ <title>Herakut Beautifies The Streets Of New York</title>
97
+ <description><![CDATA[<p><img alt="9501466.jpg" src="http://www.woostercollective.com/9501466.jpg" width="480" height="640" /></p>
98
+
99
+ <p>While they were in town this week for an opening at <a href="http://redflagg.com/">RedFlagg</a>, <a href="http://www.herakut.de/">Herakut</a> put up a wonderful piece outside <a href="http://eyebeam.org/">Eyebeam</a> If you haven't head about the show in New York, here's the info:</p>
100
+
101
+ <p>Herakut<br />
102
+ No Placebos<br />
103
+ May 21 – July 3, 2009</p>]]></description>
104
+ <link>http://feedproxy.google.com/~r/wooster/~3/rWAKvvkBDYY/herakut_beautifies_the_streets_of_new_yo.html</link>
105
+ <guid isPermaLink="false">http://www.woostercollective.com/2009/05/herakut_beautifies_the_streets_of_new_yo.html</guid>
106
+ <category>Walls</category>
107
+ <pubDate>Fri, 22 May 2009 07:40:04 -0500</pubDate>
108
+ <feedburner:origLink>http://www.woostercollective.com/2009/05/herakut_beautifies_the_streets_of_new_yo.html</feedburner:origLink>
109
+ </item>
110
+ <item>
111
+ <title>Fresh Stuff From Cena7</title>
112
+ <description><![CDATA[<p><a href="http://www.woostercollective.com/cenared.jpg"><img alt="cenared.jpg" src="http://www.woostercollective.com/cenared-thumb.jpg" width="500" height="409" /></a></p>
113
+
114
+ <p>More from Cena7 <a href="http://www.flickr.com/photos/cena7-mpc">here</a>. <br />
115
+ </p>]]></description>
116
+ <link>http://feedproxy.google.com/~r/wooster/~3/x4y4ZuKBadg/fresh_stuff_from_cena7_1.html</link>
117
+ <guid isPermaLink="false">http://www.woostercollective.com/2009/05/fresh_stuff_from_cena7_1.html</guid>
118
+ <category>Walls</category>
119
+ <pubDate>Fri, 22 May 2009 07:37:33 -0500</pubDate>
120
+ <feedburner:origLink>http://www.woostercollective.com/2009/05/fresh_stuff_from_cena7_1.html</feedburner:origLink>
121
+ </item>
122
+ <item>
123
+ <title>Wooster on Facebook</title>
124
+ <description><![CDATA[<p><img alt="woostfaceo.jpg" src="http://www.woostercollective.com/woostfaceo.jpg" width="500" height="376" /></p>
125
+
126
+ <p>Each and every day we get more excited about the discussions that are taking place on our <a href="http://www.facebook.com/pages/Wooster-Collective/24080260389?ref=ts#/pages/Wooster-Collective/24080260389?ref=mf">Wooster Collective Facebook page</a>. The responses to our recent questions:</p>
127
+
128
+ <p>For those who risk getting arrested while putting up street art or writing graffiti: What motivates you to do it?</p>
129
+
130
+ <p>What's currently inspiring you?</p>
131
+
132
+ <p>How would you describe the "role" that graffiti or street art plays in your life? What does it "add" or subtract?</p>
133
+
134
+ <p>If I gave you $50 today, with the condition that you had to spend it on "art", what would you do with it?</p>
135
+
136
+ <p>If you haven't yet checked it out we can't recommend it more highly. It's become an incredible companion to the Wooster blog. </p>
137
+
138
+ <p>Also, we are indeed on Twitter. Not as "Wooster Collective" but as <a href="http://www.twitter.com/MarcSchil">Marc</a> and <a href="http://www.twitter.com/saraschiller">Sara</a>. </p>]]></description>
139
+ <link>http://feedproxy.google.com/~r/wooster/~3/4uAvR5rZn80/wooster_on_facebook.html</link>
140
+ <guid isPermaLink="false">http://www.woostercollective.com/2009/05/wooster_on_facebook.html</guid>
141
+ <category>Site Announcements</category>
142
+ <pubDate>Thu, 21 May 2009 08:05:18 -0500</pubDate>
143
+ <feedburner:origLink>http://www.woostercollective.com/2009/05/wooster_on_facebook.html</feedburner:origLink>
144
+ </item>
145
+ <item>
146
+ <title>Wooster on Facebook</title>
147
+ <description><![CDATA[<p><img alt="woostfaceo.jpg" src="http://www.woostercollective.com/woostfaceo.jpg" width="500" height="376" /></p>
148
+
149
+ <p>Each and every day we get more excited about the discussions that are taking place on our <a href="http://www.facebook.com/pages/Wooster-Collective/24080260389?ref=ts#/pages/Wooster-Collective/24080260389?ref=mf">Wooster Collective Facebook page</a>. The responses to our recent questions:</p>
150
+
151
+ <p>For those who risk getting arrested while putting up street art or writing graffiti: What motivates you to do it?</p>
152
+
153
+ <p>What's currently inspiring you?</p>
154
+
155
+ <p>How would you describe the "role" that graffiti or street art plays in your life? What does it "add" or subtract?</p>
156
+
157
+ <p>If I gave you $50 today, with the condition that you had to spend it on "art", what would you do with it?</p>
158
+
159
+ <p>If you haven't yet checked it out we can't recommend it more highly. It's become an incredible companion to the Wooster blog. </p>
160
+
161
+ <p>Also, we are indeed on Twitter. Not as "Wooster Collective" but as <a href="http://www.twitter.com/MarcSchil">Marc</a> and <a href="http://www.twitter.com/saraschiller">Sara</a>. </p>]]></description>
162
+ <link>http://feedproxy.google.com/~r/wooster/~3/CV9qfY3WZqM/wooster_on_facebook_1.html</link>
163
+ <guid isPermaLink="false">http://www.woostercollective.com/2009/05/wooster_on_facebook_1.html</guid>
164
+ <category>Site Announcements</category>
165
+ <pubDate>Thu, 21 May 2009 08:05:18 -0500</pubDate>
166
+ <feedburner:origLink>http://www.woostercollective.com/2009/05/wooster_on_facebook_1.html</feedburner:origLink>
167
+ </item>
168
+ <item>
169
+ <title>Seen On The Steets Of Berlin</title>
170
+ <description><![CDATA[<p><a href="http://www.woostercollective.com/brownpaper1.jpg"><img alt="brownpaper1.jpg" src="http://www.woostercollective.com/brownpaper1-thumb.jpg" width="500" height="416" /></a><br />
171
+ </p>]]></description>
172
+ <link>http://feedproxy.google.com/~r/wooster/~3/O-9kaseEs8I/seen_on_the_steets_of_berlin_1.html</link>
173
+ <guid isPermaLink="false">http://www.woostercollective.com/2009/05/seen_on_the_steets_of_berlin_1.html</guid>
174
+ <category>Wheatpastes</category>
175
+ <pubDate>Thu, 21 May 2009 07:57:05 -0500</pubDate>
176
+ <feedburner:origLink>http://www.woostercollective.com/2009/05/seen_on_the_steets_of_berlin_1.html</feedburner:origLink>
177
+ </item>
178
+ <item>
179
+ <title>Checkin' In With... Lister</title>
180
+ <description><![CDATA[<p><a href="http://www.woostercollective.com/listcheck.jpg"><img alt="listcheck.jpg" src="http://www.woostercollective.com/listcheck-thumb.jpg" width="500" height="342" /></a></p>
181
+
182
+ <p>"out onto this roof the other night. i wasnt even sure that i had finished this piece but when i returned the next day i was surprised that it looked like this. the wall was so big and high that i couldn't step back at all. i am still surprised the proportions were correct."... <a href="http://www.anthonylister.com">Lister</a></p>]]></description>
183
+ <link>http://feedproxy.google.com/~r/wooster/~3/xKfEO6qRHLY/checkin_in_with_lister.html</link>
184
+ <guid isPermaLink="false">http://www.woostercollective.com/2009/05/checkin_in_with_lister.html</guid>
185
+ <category>Walls</category>
186
+ <pubDate>Thu, 21 May 2009 07:45:54 -0500</pubDate>
187
+ <feedburner:origLink>http://www.woostercollective.com/2009/05/checkin_in_with_lister.html</feedburner:origLink>
188
+ </item>
189
+ <item>
190
+ <title>Another Great Electronic Billboard Takeover From Poster Child</title>
191
+ <description><![CDATA[<p><a href="http://www.woostercollective.com/pixeljes.jpg"><img alt="pixeljes.jpg" src="http://www.woostercollective.com/pixeljes-thumb.jpg" width="500" height="375" /></a></p>
192
+
193
+ <div><object width="480" height="381"><param name="movie" value="http://www.dailymotion.com/swf/x99ave_stained-glass-postpixelators_creation&related=1"></param><param name="allowFullScreen" value="true"></param><param name="allowScriptAccess" value="always"></param><embed src="http://www.dailymotion.com/swf/x99ave_stained-glass-postpixelators_creation&related=1" type="application/x-shockwave-flash" width="480" height="381" allowFullScreen="true" allowScriptAccess="always"></embed></object><br /><b><a href="http://www.dailymotion.com/video/x99ave_stained-glass-postpixelators_creation">Stained Glass Post-Pixelators</a></b><br /><i>Uploaded by <a href="http://www.dailymotion.com/posterchild">posterchild</a>. - <a href="http://www.dailymotion.com/channel/creation">Independent web videos.</a></i></div>
194
+
195
+ <p>More from Poster Child <a href="http://www.bladediary.com/">here</a>. </p>]]></description>
196
+ <link>http://feedproxy.google.com/~r/wooster/~3/Rz_yYr1lijQ/another_great_electronic_billboard_takeo.html</link>
197
+ <guid isPermaLink="false">http://www.woostercollective.com/2009/05/another_great_electronic_billboard_takeo.html</guid>
198
+ <category>Billboard Liberations</category>
199
+ <pubDate>Wed, 20 May 2009 07:59:04 -0500</pubDate>
200
+ <feedburner:origLink>http://www.woostercollective.com/2009/05/another_great_electronic_billboard_takeo.html</feedburner:origLink>
201
+ </item>
202
+ </channel>
203
+ </rss>
@@ -0,0 +1,215 @@
1
+ <?xml version="1.0" encoding="utf-8"?>
2
+ <?xml-stylesheet type="text/xsl" media="screen" href="/~d/styles/rss2full.xsl"?><?xml-stylesheet type="text/css" media="screen" href="http://feeds2.feedburner.com/~d/styles/itemcontent.css"?><rss xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0" version="2.0">
3
+ <channel>
4
+ <title>Wooster Collective</title>
5
+ <link>http://www.woostercollective.com/</link>
6
+ <description />
7
+ <language>en</language>
8
+ <copyright>Copyright 2009</copyright>
9
+ <lastBuildDate>Mon, 25 May 2009 07:48:04 -0500</lastBuildDate>
10
+ <generator>http://www.sixapart.com/movabletype/?v=3.3</generator>
11
+ <docs>http://blogs.law.harvard.edu/tech/rss</docs>
12
+
13
+ <atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="self" href="www.woostercollective.com/rss/index.xml" type="application/rss+xml" /><feedburner:emailServiceId>wooster</feedburner:emailServiceId><feedburner:feedburnerHostname>http://feedburner.google.com</feedburner:feedburnerHostname><item>
14
+ <title>Fresh Stuff From Davide Zucco (aka Rekal)</title>
15
+ <description><![CDATA[<p><a href="http://www.woostercollective.com/rekalmonkey.jpg"><img alt="rekalmonkey.jpg" src="http://www.woostercollective.com/rekalmonkey-thumb.jpg" width="500" height="589" /></a></p>
16
+
17
+ <p>From from Rekal<a href="http://www.rekal.org"> here</a>. </p>
18
+
19
+ <p></p>
20
+
21
+ <p></p>
22
+
23
+ <p><br />
24
+ </p>]]></description>
25
+ <link>http://feedproxy.google.com/~r/wooster/~3/rlRoUehLOSM/fresh_stuff_from_davide_zucco_aka_rekal_1.html</link>
26
+ <guid isPermaLink="false">http://www.woostercollective.com/2009/05/fresh_stuff_from_davide_zucco_aka_rekal_1.html</guid>
27
+ <category>Art</category>
28
+ <pubDate>Mon, 25 May 2009 07:48:04 -0500</pubDate>
29
+ <feedburner:origLink>http://www.woostercollective.com/2009/05/fresh_stuff_from_davide_zucco_aka_rekal_1.html</feedburner:origLink></item>
30
+ <item>
31
+ <title>Fresh Stuff From Sten and Lex</title>
32
+ <description><![CDATA[<p><a href="http://www.woostercollective.com/stenlexpope.jpg"><img alt="stenlexpope.jpg" src="http://www.woostercollective.com/stenlexpope-thumb.jpg" width="500" height="364" /></a></p>
33
+
34
+ <p>Location: Palermo in Piazza Magione.<br />
35
+ Image: Pope Sergius, the first Pope born in Palermo</p>]]></description>
36
+ <link>http://feedproxy.google.com/~r/wooster/~3/RAHYCux58bs/fresh_stuff_from_sten_and_lex.html</link>
37
+ <guid isPermaLink="false">http://www.woostercollective.com/2009/05/fresh_stuff_from_sten_and_lex.html</guid>
38
+ <category>Walls</category>
39
+ <pubDate>Mon, 25 May 2009 07:44:48 -0500</pubDate>
40
+ <feedburner:origLink>http://www.woostercollective.com/2009/05/fresh_stuff_from_sten_and_lex.html</feedburner:origLink></item>
41
+ <item>
42
+ <title>Seen In North London: Department of Urban Censorship</title>
43
+ <description><![CDATA[<p><a href="http://www.woostercollective.com/urbancensor.jpg"><img alt="urbancensor.jpg" src="http://www.woostercollective.com/urbancensor-thumb.jpg" width="500" height="444" /></a></p>
44
+
45
+ <p>(Thanks, <a href="http://www.tupajumi.com/jonathan/">Jonathan</a>)<br />
46
+ </p>]]></description>
47
+ <link>http://feedproxy.google.com/~r/wooster/~3/syiP17t0PKo/seen_in_north_london_department_of_urban.html</link>
48
+ <guid isPermaLink="false">http://www.woostercollective.com/2009/05/seen_in_north_london_department_of_urban.html</guid>
49
+ <category>Wheatpastes</category>
50
+ <pubDate>Mon, 25 May 2009 07:39:16 -0500</pubDate>
51
+ <feedburner:origLink>http://www.woostercollective.com/2009/05/seen_in_north_london_department_of_urban.html</feedburner:origLink></item>
52
+ <item>
53
+ <title>Seen On The Steets Of Stockholm</title>
54
+ <description><![CDATA[<p><a href="http://www.woostercollective.com/poststock.jpg"><img alt="poststock.jpg" src="http://www.woostercollective.com/poststock-thumb.jpg" width="500" height="748" /></a></p>
55
+
56
+ <p>Artist: <a href="http://vimeo.com/post">Post</a></p>]]></description>
57
+ <link>http://feedproxy.google.com/~r/wooster/~3/611KGRn-ZQE/seen_on_the_steets_of_stockholm.html</link>
58
+ <guid isPermaLink="false">http://www.woostercollective.com/2009/05/seen_on_the_steets_of_stockholm.html</guid>
59
+ <category>Wheatpastes</category>
60
+ <pubDate>Mon, 25 May 2009 07:29:55 -0500</pubDate>
61
+ <feedburner:origLink>http://www.woostercollective.com/2009/05/seen_on_the_steets_of_stockholm.html</feedburner:origLink></item>
62
+ <item>
63
+ <title>Zevs' Electric Rainbows (2007)</title>
64
+ <description><![CDATA[<p>Zevs has updated <a href="http://www.gzzglz.com/">his website</a> with photos of past and recent project. We love the Electric Rainbows that he exchanged for advertisements back 2007:</p>
65
+
66
+ <p><a href="http://www.woostercollective.com/electric-rainbow-02.jpg"><img alt="electric-rainbow-02.jpg" src="http://www.woostercollective.com/electric-rainbow-02-thumb.jpg" width="500" height="335" /></a></p>
67
+
68
+ <p><br />
69
+ </p>]]></description>
70
+ <link>http://feedproxy.google.com/~r/wooster/~3/TpmlTS7y0f0/zevs_electric_rainbows_2007.html</link>
71
+ <guid isPermaLink="false">http://www.woostercollective.com/2009/05/zevs_electric_rainbows_2007.html</guid>
72
+ <category>Art</category>
73
+ <pubDate>Mon, 25 May 2009 07:19:27 -0500</pubDate>
74
+ <feedburner:origLink>http://www.woostercollective.com/2009/05/zevs_electric_rainbows_2007.html</feedburner:origLink></item>
75
+ <item>
76
+ <title>Zephyr - An Introduction</title>
77
+ <description><![CDATA[<p><a href="http://www.woostercollective.com/zephydith.jpg"><img alt="zephydith.jpg" src="http://www.woostercollective.com/zephydith-thumb.jpg" width="500" height="347" /></a></p>
78
+
79
+ <p><object width="320" height="265"><param name="movie" value="http://www.youtube.com/v/kqXXZcy-2JU&hl=en&fs=1&rel=0&hd=1"></param><param name="allowFullScreen" value="true"></param><param name="allowscriptaccess" value="always"></param><embed src="http://www.youtube.com/v/kqXXZcy-2JU&hl=en&fs=1&rel=0&hd=1" type="application/x-shockwave-flash" allowscriptaccess="always" allowfullscreen="true" width="320" height="265"></embed></object></p>
80
+
81
+ <p>One of the first graffiti artists to "accept" Sara and I back in 2000 when we started the Wooster Collective was Zephyr. This week <a href="http://www.upperplayground.com/">Upper Playground</a> posted a terrific interview with Zephyr filmed a few years ago for their Dithers DVD. </p>]]></description>
82
+ <link>http://feedproxy.google.com/~r/wooster/~3/32-ED1oPdYY/zephyr_in_introduction.html</link>
83
+ <guid isPermaLink="false">http://www.woostercollective.com/2009/05/zephyr_in_introduction.html</guid>
84
+ <category>Graffiti</category>
85
+ <pubDate>Sat, 23 May 2009 12:44:36 -0500</pubDate>
86
+ <feedburner:origLink>http://www.woostercollective.com/2009/05/zephyr_in_introduction.html</feedburner:origLink></item>
87
+ <item>
88
+ <title>The Yes Men - An Introduction</title>
89
+ <description><![CDATA[<div><object width="512" height="322"><param name="movie" value="http://d.yimg.com/static.video.yahoo.com/yep/YV_YEP.swf?ver=2.2.40" /><param name="allowFullScreen" value="true" /><param name="AllowScriptAccess" VALUE="always" /><param name="bgcolor" value="#000000" /><param name="flashVars" value="id=13527850&vid=13527850&lang=en-us&intl=us&thumbUrl=&embed=1" /><embed src="http://d.yimg.com/static.video.yahoo.com/yep/YV_YEP.swf?ver=2.2.40" type="application/x-shockwave-flash" width="512" height="322" allowFullScreen="true" AllowScriptAccess="always" bgcolor="#000000" flashVars="id=13527850&vid=13527850&lang=en-us&intl=us&thumbUrl=&embed=1" ></embed></object><br /><a href="http://video.yahoo.com/watch/13527850/13527850"></a> @ <a href="http://video.yahoo.com" >Yahoo! Video</a></div>
90
+
91
+ <p>If you're not familiar with <a href="http://theyesmen.org/">The Yes Men</a>, this video of their Poptech speech from 2006 is a fantastic introduction.</p>
92
+
93
+ <p>(Hat tip to <a href="http://www.brainpickings.org/">Brain Pickings</a>)</p>]]></description>
94
+ <link>http://feedproxy.google.com/~r/wooster/~3/0yXb4Ui8C3I/the_yes_men_an_introduction.html</link>
95
+ <guid isPermaLink="false">http://www.woostercollective.com/2009/05/the_yes_men_an_introduction.html</guid>
96
+ <category>Activism</category>
97
+ <pubDate>Fri, 22 May 2009 11:24:26 -0500</pubDate>
98
+ <feedburner:origLink>http://www.woostercollective.com/2009/05/the_yes_men_an_introduction.html</feedburner:origLink></item>
99
+ <item>
100
+ <title>A Great Example Of Site Specific Work: Carlsbad Sidewalk Surfer</title>
101
+ <description><![CDATA[<p><img alt="bryansurfer.jpg" src="http://www.woostercollective.com/bryansurfer.jpg" width="500" height="384" /></p>
102
+
103
+ <p><object width="400" height="307"><param name="allowfullscreen" value="true" /><param name="allowscriptaccess" value="always" /><param name="movie" value="http://vimeo.com/moogaloop.swf?clip_id=4772503&amp;server=vimeo.com&amp;show_title=1&amp;show_byline=1&amp;show_portrait=0&amp;color=&amp;fullscreen=1" /><embed src="http://vimeo.com/moogaloop.swf?clip_id=4772503&amp;server=vimeo.com&amp;show_title=1&amp;show_byline=1&amp;show_portrait=0&amp;color=&amp;fullscreen=1" type="application/x-shockwave-flash" allowfullscreen="true" allowscriptaccess="always" width="400" height="307"></embed></object><p><a href="http://vimeo.com/4772503">Carlsbad Sidewalk Surfer by bryan snyder</a> from <a href="http://vimeo.com/bryansnyder">Bryan Snyder</a> on <a href="http://vimeo.com">Vimeo</a>.</p></p>
104
+
105
+ <p>From <a href="http://www.snyderartdesign.com">Bryan</a>:</p>
106
+
107
+ <p>Carlsbad Sidewalk Surfer interacts with its environment during mid day wind gusts. Like the waves that tumble upon our shore, the visibility of this art piece depends on the weather. Between the glassy conditions of the morning and the mellow winds at dusk, the Carlsbad Sidewalk Surfer sneaks into another ride. The goal of this project is to showcase the relationship between a piece of art and its environment when placed in the streets."</p>]]></description>
108
+ <link>http://feedproxy.google.com/~r/wooster/~3/oXfLFSd86cY/a_great_example_of_site_specific_work_ca.html</link>
109
+ <guid isPermaLink="false">http://www.woostercollective.com/2009/05/a_great_example_of_site_specific_work_ca.html</guid>
110
+ <category>Environmental</category>
111
+ <pubDate>Fri, 22 May 2009 07:54:36 -0500</pubDate>
112
+ <feedburner:origLink>http://www.woostercollective.com/2009/05/a_great_example_of_site_specific_work_ca.html</feedburner:origLink></item>
113
+ <item>
114
+ <title>Herakut Beautifies The Streets Of New York</title>
115
+ <description><![CDATA[<p><img alt="9501466.jpg" src="http://www.woostercollective.com/9501466.jpg" width="480" height="640" /></p>
116
+
117
+ <p>While they were in town this week for an opening at <a href="http://redflagg.com/">RedFlagg</a>, <a href="http://www.herakut.de/">Herakut</a> put up a wonderful piece outside <a href="http://eyebeam.org/">Eyebeam</a> If you haven't head about the show in New York, here's the info:</p>
118
+
119
+ <p>Herakut<br />
120
+ No Placebos<br />
121
+ May 21 – July 3, 2009</p>]]></description>
122
+ <link>http://feedproxy.google.com/~r/wooster/~3/rWAKvvkBDYY/herakut_beautifies_the_streets_of_new_yo.html</link>
123
+ <guid isPermaLink="false">http://www.woostercollective.com/2009/05/herakut_beautifies_the_streets_of_new_yo.html</guid>
124
+ <category>Walls</category>
125
+ <pubDate>Fri, 22 May 2009 07:40:04 -0500</pubDate>
126
+ <feedburner:origLink>http://www.woostercollective.com/2009/05/herakut_beautifies_the_streets_of_new_yo.html</feedburner:origLink></item>
127
+ <item>
128
+ <title>Fresh Stuff From Cena7</title>
129
+ <description><![CDATA[<p><a href="http://www.woostercollective.com/cenared.jpg"><img alt="cenared.jpg" src="http://www.woostercollective.com/cenared-thumb.jpg" width="500" height="409" /></a></p>
130
+
131
+ <p>More from Cena7 <a href="http://www.flickr.com/photos/cena7-mpc">here</a>. <br />
132
+ </p>]]></description>
133
+ <link>http://feedproxy.google.com/~r/wooster/~3/x4y4ZuKBadg/fresh_stuff_from_cena7_1.html</link>
134
+ <guid isPermaLink="false">http://www.woostercollective.com/2009/05/fresh_stuff_from_cena7_1.html</guid>
135
+ <category>Walls</category>
136
+ <pubDate>Fri, 22 May 2009 07:37:33 -0500</pubDate>
137
+ <feedburner:origLink>http://www.woostercollective.com/2009/05/fresh_stuff_from_cena7_1.html</feedburner:origLink></item>
138
+ <item>
139
+ <title>Wooster on Facebook</title>
140
+ <description><![CDATA[<p><img alt="woostfaceo.jpg" src="http://www.woostercollective.com/woostfaceo.jpg" width="500" height="376" /></p>
141
+
142
+ <p>Each and every day we get more excited about the discussions that are taking place on our <a href="http://www.facebook.com/pages/Wooster-Collective/24080260389?ref=ts#/pages/Wooster-Collective/24080260389?ref=mf">Wooster Collective Facebook page</a>. The responses to our recent questions:</p>
143
+
144
+ <p>For those who risk getting arrested while putting up street art or writing graffiti: What motivates you to do it?</p>
145
+
146
+ <p>What's currently inspiring you?</p>
147
+
148
+ <p>How would you describe the "role" that graffiti or street art plays in your life? What does it "add" or subtract?</p>
149
+
150
+ <p>If I gave you $50 today, with the condition that you had to spend it on "art", what would you do with it?</p>
151
+
152
+ <p>If you haven't yet checked it out we can't recommend it more highly. It's become an incredible companion to the Wooster blog. </p>
153
+
154
+ <p>Also, we are indeed on Twitter. Not as "Wooster Collective" but as <a href="http://www.twitter.com/MarcSchil">Marc</a> and <a href="http://www.twitter.com/saraschiller">Sara</a>. </p>]]></description>
155
+ <link>http://feedproxy.google.com/~r/wooster/~3/4uAvR5rZn80/wooster_on_facebook.html</link>
156
+ <guid isPermaLink="false">http://www.woostercollective.com/2009/05/wooster_on_facebook.html</guid>
157
+ <category>Site Announcements</category>
158
+ <pubDate>Thu, 21 May 2009 08:05:18 -0500</pubDate>
159
+ <feedburner:origLink>http://www.woostercollective.com/2009/05/wooster_on_facebook.html</feedburner:origLink></item>
160
+ <item>
161
+ <title>Wooster on Facebook</title>
162
+ <description><![CDATA[<p><img alt="woostfaceo.jpg" src="http://www.woostercollective.com/woostfaceo.jpg" width="500" height="376" /></p>
163
+
164
+ <p>Each and every day we get more excited about the discussions that are taking place on our <a href="http://www.facebook.com/pages/Wooster-Collective/24080260389?ref=ts#/pages/Wooster-Collective/24080260389?ref=mf">Wooster Collective Facebook page</a>. The responses to our recent questions:</p>
165
+
166
+ <p>For those who risk getting arrested while putting up street art or writing graffiti: What motivates you to do it?</p>
167
+
168
+ <p>What's currently inspiring you?</p>
169
+
170
+ <p>How would you describe the "role" that graffiti or street art plays in your life? What does it "add" or subtract?</p>
171
+
172
+ <p>If I gave you $50 today, with the condition that you had to spend it on "art", what would you do with it?</p>
173
+
174
+ <p>If you haven't yet checked it out we can't recommend it more highly. It's become an incredible companion to the Wooster blog. </p>
175
+
176
+ <p>Also, we are indeed on Twitter. Not as "Wooster Collective" but as <a href="http://www.twitter.com/MarcSchil">Marc</a> and <a href="http://www.twitter.com/saraschiller">Sara</a>. </p>]]></description>
177
+ <link>http://feedproxy.google.com/~r/wooster/~3/CV9qfY3WZqM/wooster_on_facebook_1.html</link>
178
+ <guid isPermaLink="false">http://www.woostercollective.com/2009/05/wooster_on_facebook_1.html</guid>
179
+ <category>Site Announcements</category>
180
+ <pubDate>Thu, 21 May 2009 08:05:18 -0500</pubDate>
181
+ <feedburner:origLink>http://www.woostercollective.com/2009/05/wooster_on_facebook_1.html</feedburner:origLink></item>
182
+ <item>
183
+ <title>Seen On The Steets Of Berlin</title>
184
+ <description><![CDATA[<p><a href="http://www.woostercollective.com/brownpaper1.jpg"><img alt="brownpaper1.jpg" src="http://www.woostercollective.com/brownpaper1-thumb.jpg" width="500" height="416" /></a><br />
185
+ </p>]]></description>
186
+ <link>http://feedproxy.google.com/~r/wooster/~3/O-9kaseEs8I/seen_on_the_steets_of_berlin_1.html</link>
187
+ <guid isPermaLink="false">http://www.woostercollective.com/2009/05/seen_on_the_steets_of_berlin_1.html</guid>
188
+ <category>Wheatpastes</category>
189
+ <pubDate>Thu, 21 May 2009 07:57:05 -0500</pubDate>
190
+ <feedburner:origLink>http://www.woostercollective.com/2009/05/seen_on_the_steets_of_berlin_1.html</feedburner:origLink></item>
191
+ <item>
192
+ <title>Checkin' In With... Lister</title>
193
+ <description><![CDATA[<p><a href="http://www.woostercollective.com/listcheck.jpg"><img alt="listcheck.jpg" src="http://www.woostercollective.com/listcheck-thumb.jpg" width="500" height="342" /></a></p>
194
+
195
+ <p>"out onto this roof the other night. i wasnt even sure that i had finished this piece but when i returned the next day i was surprised that it looked like this. the wall was so big and high that i couldn't step back at all. i am still surprised the proportions were correct."... <a href="http://www.anthonylister.com">Lister</a></p>]]></description>
196
+ <link>http://feedproxy.google.com/~r/wooster/~3/xKfEO6qRHLY/checkin_in_with_lister.html</link>
197
+ <guid isPermaLink="false">http://www.woostercollective.com/2009/05/checkin_in_with_lister.html</guid>
198
+ <category>Walls</category>
199
+ <pubDate>Thu, 21 May 2009 07:45:54 -0500</pubDate>
200
+ <feedburner:origLink>http://www.woostercollective.com/2009/05/checkin_in_with_lister.html</feedburner:origLink></item>
201
+ <item>
202
+ <title>Another Great Electronic Billboard Takeover From Poster Child</title>
203
+ <description><![CDATA[<p><a href="http://www.woostercollective.com/pixeljes.jpg"><img alt="pixeljes.jpg" src="http://www.woostercollective.com/pixeljes-thumb.jpg" width="500" height="375" /></a></p>
204
+
205
+ <div><object width="480" height="381"><param name="movie" value="http://www.dailymotion.com/swf/x99ave_stained-glass-postpixelators_creation&related=1"></param><param name="allowFullScreen" value="true"></param><param name="allowScriptAccess" value="always"></param><embed src="http://www.dailymotion.com/swf/x99ave_stained-glass-postpixelators_creation&related=1" type="application/x-shockwave-flash" width="480" height="381" allowFullScreen="true" allowScriptAccess="always"></embed></object><br /><b><a href="http://www.dailymotion.com/video/x99ave_stained-glass-postpixelators_creation">Stained Glass Post-Pixelators</a></b><br /><i>Uploaded by <a href="http://www.dailymotion.com/posterchild">posterchild</a>. - <a href="http://www.dailymotion.com/channel/creation">Independent web videos.</a></i></div>
206
+
207
+ <p>More from Poster Child <a href="http://www.bladediary.com/">here</a>. </p>]]></description>
208
+ <link>http://feedproxy.google.com/~r/wooster/~3/Rz_yYr1lijQ/another_great_electronic_billboard_takeo.html</link>
209
+ <guid isPermaLink="false">http://www.woostercollective.com/2009/05/another_great_electronic_billboard_takeo.html</guid>
210
+ <category>Billboard Liberations</category>
211
+ <pubDate>Wed, 20 May 2009 07:59:04 -0500</pubDate>
212
+ <feedburner:origLink>http://www.woostercollective.com/2009/05/another_great_electronic_billboard_takeo.html</feedburner:origLink></item>
213
+
214
+ </channel>
215
+ </rss>
@@ -0,0 +1,28 @@
1
+ require 'rubygems'
2
+ require 'mocha'
3
+ require 'spec'
4
+
5
+ require File.join(File.dirname(__FILE__), %w[.. lib feedtosis])
6
+
7
+ Spec::Runner.configure do |config|
8
+ config.mock_with(:mocha)
9
+ end
10
+
11
+ # Returns the xml fixture identified by +name+.
12
+ def xml_fixture(name)
13
+ File.read(File.join(File.dirname(__FILE__), 'fixtures', 'xml', "#{name}.xml"))
14
+ end
15
+
16
+ def http_header(name)
17
+ File.read(File.join(File.dirname(__FILE__), 'fixtures', 'http_headers', "#{name}.txt"))
18
+ end
19
+
20
+ shared_examples_for "all backends" do
21
+ it "should respond to #get" do
22
+ @backend.should respond_to(:get)
23
+ end
24
+
25
+ it "should respond to #set" do
26
+ @backend.should respond_to(:set)
27
+ end
28
+ end
metadata ADDED
@@ -0,0 +1,118 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: jsl-feedtosis
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.2.3
5
+ platform: ruby
6
+ authors:
7
+ - Justin Leitgeb
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2009-05-22 00:00:00 -07:00
13
+ default_executable:
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: taf2-curb
17
+ type: :runtime
18
+ version_requirement:
19
+ version_requirements: !ruby/object:Gem::Requirement
20
+ requirements:
21
+ - - ">="
22
+ - !ruby/object:Gem::Version
23
+ version: "0"
24
+ version:
25
+ - !ruby/object:Gem::Dependency
26
+ name: jsl-moneta
27
+ type: :runtime
28
+ version_requirement:
29
+ version_requirements: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: "0"
34
+ version:
35
+ - !ruby/object:Gem::Dependency
36
+ name: jsl-http_headers
37
+ type: :runtime
38
+ version_requirement:
39
+ version_requirements: !ruby/object:Gem::Requirement
40
+ requirements:
41
+ - - ">="
42
+ - !ruby/object:Gem::Version
43
+ version: "0"
44
+ version:
45
+ - !ruby/object:Gem::Dependency
46
+ name: namelessjon-feed_me
47
+ type: :runtime
48
+ version_requirement:
49
+ version_requirements: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - ">="
52
+ - !ruby/object:Gem::Version
53
+ version: "0"
54
+ version:
55
+ description: Feedtosis finds new information in feeds quickly using smart fetching and matching of previously read entries
56
+ email: justin@phq.org
57
+ executables: []
58
+
59
+ extensions: []
60
+
61
+ extra_rdoc_files:
62
+ - README.rdoc
63
+ files:
64
+ - lib/extensions/core/array.rb
65
+ - lib/extensions/core/hash.rb
66
+ - lib/extensions/feed_normalizer/feed_instance_methods.rb
67
+ - lib/feedtosis/result.rb
68
+ - lib/feedtosis/client.rb
69
+ - lib/feedtosis.rb
70
+ - LICENSE
71
+ - feedtosis.gemspec
72
+ - Rakefile
73
+ - README.rdoc
74
+ - spec/extensions/feed_normalizer/feed_instance_methods_spec.rb
75
+ - spec/fixtures/http_headers/wooster.txt
76
+ - spec/fixtures/xml/older_wooster.xml
77
+ - spec/fixtures/xml/wooster.xml
78
+ - spec/feedtosis/client_spec.rb
79
+ - spec/feedtosis_spec.rb
80
+ - spec/feedtosis/result_spec.rb
81
+ - spec/spec_helper.rb
82
+ has_rdoc: true
83
+ homepage: http://github.com/jsl/feedtosis
84
+ post_install_message:
85
+ rdoc_options:
86
+ - --charset=UTF-8
87
+ - --title
88
+ - Feedtosis
89
+ - --main
90
+ - README.rdoc
91
+ - --line-numbers
92
+ - --inline-source
93
+ require_paths:
94
+ - lib
95
+ required_ruby_version: !ruby/object:Gem::Requirement
96
+ requirements:
97
+ - - ">="
98
+ - !ruby/object:Gem::Version
99
+ version: "0"
100
+ version:
101
+ required_rubygems_version: !ruby/object:Gem::Requirement
102
+ requirements:
103
+ - - ">="
104
+ - !ruby/object:Gem::Version
105
+ version: "0"
106
+ version:
107
+ requirements: []
108
+
109
+ rubyforge_project:
110
+ rubygems_version: 1.2.0
111
+ signing_key:
112
+ specification_version: 2
113
+ summary: Retrieves feeds using conditional GET and marks entries that you haven't seen before
114
+ test_files:
115
+ - spec/feedtosis_spec.rb
116
+ - spec/spec_helper.rb
117
+ - spec/feedtosis/client_spec.rb
118
+ - spec/feedtosis/result_spec.rb