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 +20 -0
- data/README.rdoc +141 -0
- data/Rakefile +28 -0
- data/feedtosis.gemspec +49 -0
- data/lib/extensions/core/array.rb +8 -0
- data/lib/extensions/core/hash.rb +17 -0
- data/lib/extensions/feed_normalizer/feed_instance_methods.rb +17 -0
- data/lib/feedtosis/client.rb +146 -0
- data/lib/feedtosis/result.rb +48 -0
- data/lib/feedtosis.rb +17 -0
- data/spec/extensions/feed_normalizer/feed_instance_methods_spec.rb +12 -0
- data/spec/feedtosis/client_spec.rb +110 -0
- data/spec/feedtosis/result_spec.rb +34 -0
- data/spec/feedtosis_spec.rb +5 -0
- data/spec/fixtures/http_headers/wooster.txt +19 -0
- data/spec/fixtures/xml/older_wooster.xml +203 -0
- data/spec/fixtures/xml/wooster.xml +215 -0
- data/spec/spec_helper.rb +28 -0
- metadata +118 -0
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,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,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&server=vimeo.com&show_title=1&show_byline=1&show_portrait=0&color=&fullscreen=1" /><embed src="http://vimeo.com/moogaloop.swf?clip_id=4772503&server=vimeo.com&show_title=1&show_byline=1&show_portrait=0&color=&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&server=vimeo.com&show_title=1&show_byline=1&show_portrait=0&color=&fullscreen=1" /><embed src="http://vimeo.com/moogaloop.swf?clip_id=4772503&server=vimeo.com&show_title=1&show_byline=1&show_portrait=0&color=&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>
|
data/spec/spec_helper.rb
ADDED
@@ -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
|