facebook_wall 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +7 -0
- data/Gemfile +4 -0
- data/README.rdoc +66 -0
- data/Rakefile +18 -0
- data/facebook_wall.gemspec +24 -0
- data/lib/facebook_wall.rb +12 -0
- data/lib/facebook_wall/factories/methods.rb +34 -0
- data/lib/facebook_wall/feed_entry_filters/chain.rb +14 -0
- data/lib/facebook_wall/feed_entry_filters/feed_entry_filter.rb +10 -0
- data/lib/facebook_wall/feed_entry_filters/link_rewriter.rb +16 -0
- data/lib/facebook_wall/feed_entry_filters/paragraphizer.rb +21 -0
- data/lib/facebook_wall/methods.rb +21 -0
- data/lib/facebook_wall/post.rb +23 -0
- data/lib/facebook_wall/std_lib_ext.rb +1 -0
- data/lib/facebook_wall/std_lib_ext/rss/parser.rb +15 -0
- data/lib/facebook_wall/version.rb +3 -0
- data/test/abstract_unit.rb +33 -0
- data/test/facebook_wall_test.rb +14 -0
- data/test/factories/factories_test.rb +112 -0
- data/test/feed_entry_filters/chain_test.rb +38 -0
- data/test/feed_entry_filters/feed_entry_filter_test.rb +7 -0
- data/test/feed_entry_filters/link_rewriter_test.rb +20 -0
- data/test/feed_entry_filters/paragraphizer_test.rb +20 -0
- data/test/post_test.rb +39 -0
- data/test/std_lib_ext/rss/parser_test.rb +9 -0
- metadata +84 -0
data/.gitignore
ADDED
data/Gemfile
ADDED
data/README.rdoc
ADDED
@@ -0,0 +1,66 @@
|
|
1
|
+
= facebook_wall
|
2
|
+
|
3
|
+
facebook_wall is a very simple library that fetches the latest wall-posts for a Facebook page without having to log in -
|
4
|
+
wall posts are taken from the page's RSS feed. The markup in posts is filtered to help ensure standards-compliance and
|
5
|
+
prevent errors when inserted into your own Web pages.
|
6
|
+
|
7
|
+
== Requirements
|
8
|
+
|
9
|
+
facebook_wall was written and tested on Ruby 1.9.2 but is likely to work on other versions. It requires no 3rd-party
|
10
|
+
libraries - it uses the open-uri and RSS libs from the Standard Library.
|
11
|
+
|
12
|
+
== Installation
|
13
|
+
|
14
|
+
Using Bundler:
|
15
|
+
|
16
|
+
gem 'facebook_wall'
|
17
|
+
|
18
|
+
or:
|
19
|
+
|
20
|
+
gem 'facebook_wall', :git => 'git://github.com/archaichorizon/ruby-facebook-wall.git'
|
21
|
+
|
22
|
+
== Usage
|
23
|
+
|
24
|
+
The following example prints the latest wall-post from
|
25
|
+
{Archaic Horizon's Facebook page}[http://www.facebook.com/archaichorizon].
|
26
|
+
|
27
|
+
require 'facebook_wall'
|
28
|
+
|
29
|
+
facebook_wall_posts = FacebookWall::posts_by 260041833284
|
30
|
+
latest_wall_post = facebook_wall_posts.first
|
31
|
+
|
32
|
+
puts latest_wall_post.description
|
33
|
+
|
34
|
+
<tt>FacebookWall::posts_by</tt> returns an <tt>Array</tt> containing instances of <tt>FacebookWall::Post</tt>.
|
35
|
+
<tt>FacebookWall::Post</tt> decorates <tt>RSS::Rss::Channel::Item</tt>, which means you can easily access all
|
36
|
+
sub-elements of <tt>item</tt>s published in a page's feed.
|
37
|
+
For example:
|
38
|
+
|
39
|
+
post.title # => A title automatically generated by Facebook
|
40
|
+
post.link # => The URL of the wall post
|
41
|
+
post.description # => The content of the wall post
|
42
|
+
post.pubDate # => The date the post was published
|
43
|
+
post.author # => The author of the post
|
44
|
+
|
45
|
+
== Filters
|
46
|
+
|
47
|
+
The library applies two filters to each item in the feed:
|
48
|
+
|
49
|
+
* <tt>FacebookWall::FeedEntryFilters::LinkRewriter</tt> completely rewrites HTML links in items, removing superfluous
|
50
|
+
attributes added by Facebook.
|
51
|
+
* <tt>FacebookWall::FeedEntryFilters::Paragraphizer</tt> wraps 'plain' paragraphs with <tt>P</tt> tags to create
|
52
|
+
semantically-correct HTML.
|
53
|
+
|
54
|
+
You can insert your own filters into the chain by adding subclasses of
|
55
|
+
<tt>FacebookWall::FeedEntryFilters::FeedEntryFilter</tt> to the <tt>FacebookWall::FeedEntryFilters</tt> module. The
|
56
|
+
following filter, for example, would append the word "foo" to the description of each item.
|
57
|
+
|
58
|
+
module FacebookWall
|
59
|
+
module FeedEntryFilters
|
60
|
+
class CustomFilter < FeedEntryFilter
|
61
|
+
def apply!(feed_entry)
|
62
|
+
feed_entry.description << 'foo'
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
data/Rakefile
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
require 'bundler/gem_tasks'
|
2
|
+
require 'rake/testtask'
|
3
|
+
require 'rake/rdoctask'
|
4
|
+
|
5
|
+
task :default => :test
|
6
|
+
|
7
|
+
Rake::TestTask.new do |t|
|
8
|
+
t.libs << 'test'
|
9
|
+
t.test_files = Dir.glob("#{File.dirname(__FILE__)}/test/**/*_test.rb").sort
|
10
|
+
t.warning = true
|
11
|
+
t.verbose = true
|
12
|
+
end
|
13
|
+
|
14
|
+
Rake::RDocTask.new do |rd|
|
15
|
+
rd.main = 'README.rdoc'
|
16
|
+
rd.rdoc_files.include 'README.rdoc', 'lib/**/*.rb'
|
17
|
+
rd.rdoc_dir = 'doc'
|
18
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
require "facebook_wall/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = "facebook_wall"
|
7
|
+
s.version = FacebookWall::VERSION
|
8
|
+
s.authors = ["Dan Bettles"]
|
9
|
+
s.email = ["dan@archaichorizon.com"]
|
10
|
+
s.homepage = "http://www.archaichorizon.com/"
|
11
|
+
s.summary = %q{facebook_wall is a very simple library that fetches the latest wall-posts for a Facebook page without having to log in - wall posts are taken from the page's RSS feed.}
|
12
|
+
s.description = %q{facebook_wall is a very simple library that fetches the latest wall-posts for a Facebook page without having to log in - wall posts are taken from the page's RSS feed. The markup in posts is filtered to help ensure standards-compliance and prevent errors when inserted into your own Web pages.}
|
13
|
+
|
14
|
+
s.rubyforge_project = "facebook_wall"
|
15
|
+
|
16
|
+
s.files = `git ls-files`.split("\n")
|
17
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
18
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
19
|
+
s.require_paths = ["lib"]
|
20
|
+
|
21
|
+
# specify any dependencies here; for example:
|
22
|
+
# s.add_development_dependency "rspec"
|
23
|
+
#s.add_runtime_dependency 'feedzirra', '~> 0.1.3'
|
24
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
require 'open-uri'
|
2
|
+
require 'rss/2.0'
|
3
|
+
|
4
|
+
require 'facebook_wall/std_lib_ext'
|
5
|
+
require 'facebook_wall/version'
|
6
|
+
require 'facebook_wall/methods'
|
7
|
+
require 'facebook_wall/post'
|
8
|
+
require 'facebook_wall/feed_entry_filters/feed_entry_filter'
|
9
|
+
require 'facebook_wall/feed_entry_filters/paragraphizer'
|
10
|
+
require 'facebook_wall/feed_entry_filters/link_rewriter'
|
11
|
+
require 'facebook_wall/feed_entry_filters/chain'
|
12
|
+
require 'facebook_wall/factories/methods'
|
@@ -0,0 +1,34 @@
|
|
1
|
+
module FacebookWall
|
2
|
+
module Factories
|
3
|
+
#Creates a filter chain containing instances of all feed-entry filters (subclasses of
|
4
|
+
#FacebookWall::FeedEntryFilters::FeedEntryFilter) in FacebookWall::FeedEntryFilters.
|
5
|
+
#=== Returns
|
6
|
+
#FacebookWall::FeedEntryFilters::Chain
|
7
|
+
def self.create_feed_entry_filter_chain
|
8
|
+
feed_entry_filters_module = FacebookWall::FeedEntryFilters
|
9
|
+
|
10
|
+
chain = feed_entry_filters_module::Chain.new
|
11
|
+
|
12
|
+
feed_entry_filters_module.constants.each do |name|
|
13
|
+
constant = feed_entry_filters_module.const_get name
|
14
|
+
chain << constant.new if constant < feed_entry_filters_module::FeedEntryFilter
|
15
|
+
end
|
16
|
+
|
17
|
+
chain
|
18
|
+
end
|
19
|
+
|
20
|
+
#Creates wall posts from the <tt>item</tt>s in the specified RSS feed.
|
21
|
+
#=== Parameters
|
22
|
+
#[feed - RSS::Rss] A RSS feed
|
23
|
+
#[feed_entry_filter_chain - FacebookWall::FeedEntryFilters::Chain] A filter chain
|
24
|
+
#=== Returns
|
25
|
+
#Array of FacebookWall::Post objects
|
26
|
+
def self.create_posts(feed, feed_entry_filter_chain)
|
27
|
+
feed.items.collect do |entry|
|
28
|
+
entry_clone = entry.clone #TODO Remove this?
|
29
|
+
feed_entry_filter_chain.apply_filters_to! entry_clone
|
30
|
+
FacebookWall::Post.new entry_clone
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
module FacebookWall
|
2
|
+
module FeedEntryFilters
|
3
|
+
class Chain < Array
|
4
|
+
#Applies the filters in the chain to the specified RSS item.
|
5
|
+
#=== Parameters
|
6
|
+
#[feed_entry - RSS::Rss::Channel::Item] A RSS item
|
7
|
+
def apply_filters_to!(feed_entry)
|
8
|
+
each do |feed_entry_filter|
|
9
|
+
feed_entry_filter.apply! feed_entry
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,10 @@
|
|
1
|
+
module FacebookWall
|
2
|
+
module FeedEntryFilters
|
3
|
+
class FeedEntryFilter
|
4
|
+
#Applies the filter to the specified RSS item. This method should be overridden in subclasses.
|
5
|
+
#=== Parameters
|
6
|
+
#[feed_entry - RSS::Rss::Channel::Item] A RSS item
|
7
|
+
def apply!(feed_entry); end
|
8
|
+
end
|
9
|
+
end
|
10
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module FacebookWall
|
2
|
+
module FeedEntryFilters
|
3
|
+
#Completely rewrites HTML links in <tt>item</tt>s, removing superfluous attributes added by Facebook. For example,
|
4
|
+
# <a href="http://example.com" target="_blank" onmousedown="doSomething()" rel="nofollow" id="link">example.com</a>
|
5
|
+
#would be replaced with:
|
6
|
+
# <a href="http://example.com">example.com</a>
|
7
|
+
class LinkRewriter < FeedEntryFilter
|
8
|
+
def apply!(feed_entry)
|
9
|
+
feed_entry.description.scan(/<a\s+[^>]+>/).each do |link_tag|
|
10
|
+
href_matches = link_tag.match(/\s+(href="[^"]*")/)
|
11
|
+
feed_entry.description.gsub! link_tag, "<a #{href_matches[1]}>" if href_matches
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module FacebookWall
|
2
|
+
module FeedEntryFilters
|
3
|
+
#Builds HTML paragraphs from groups of text separated by multiple +BR+ tags. For example:
|
4
|
+
# First paragraph<br /> <br /> Second paragraph<br/><br/><br/>Something inserted<br/>by Facebook
|
5
|
+
#would be replaced with:
|
6
|
+
# <p>First paragraph</p><p>Second paragraph</p><p>Something inserted<br/>by Facebook</p>
|
7
|
+
class Paragraphizer < FeedEntryFilter
|
8
|
+
def apply!(feed_entry)
|
9
|
+
plain_paragraphs = Paragraphizer.normalize_line_breaks(feed_entry.description).split(/(?:<br\/>\s*){2,}/)
|
10
|
+
paragraphized = plain_paragraphs.collect{|plain_paragraph| "<p>#{plain_paragraph}</p>"}.join
|
11
|
+
feed_entry.description = paragraphized
|
12
|
+
end
|
13
|
+
|
14
|
+
private
|
15
|
+
|
16
|
+
def self.normalize_line_breaks(str)
|
17
|
+
str.gsub(/<br\s*\/>/, '<br/>')
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module FacebookWall
|
2
|
+
#Returns the URL of the RSS feed for the specified Facebook page's wall.
|
3
|
+
#=== Parameters
|
4
|
+
#[id - Fixnum] The ID of the Facebook page
|
5
|
+
#=== Returns
|
6
|
+
#String
|
7
|
+
def self.feed_url(id)
|
8
|
+
"http://www.facebook.com/feeds/page.php?id=#{id.to_s}&format=rss20"
|
9
|
+
end
|
10
|
+
|
11
|
+
#Returns the most recent posts from the specified Facebook page's wall.
|
12
|
+
#=== Parameters
|
13
|
+
#[id - Fixnum] The ID of the Facebook page
|
14
|
+
#=== Returns
|
15
|
+
#Array of FacebookWall::Post objects
|
16
|
+
def self.posts_by(id)
|
17
|
+
feed = RSS::Parser.fetch_and_parse feed_url id
|
18
|
+
feed_entry_filter_chain = Factories::create_feed_entry_filter_chain
|
19
|
+
Factories::create_posts feed, feed_entry_filter_chain
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module FacebookWall
|
2
|
+
#A Facebook wall post.
|
3
|
+
#
|
4
|
+
#FacebookWall::Post decorates RSS::Rss::Channel::Item, which means you can easily access all sub-elements of
|
5
|
+
#<tt>item</tt>s published in a page's feed. For example:
|
6
|
+
#
|
7
|
+
# post.title # => A title automatically generated by Facebook
|
8
|
+
# post.link # => The URL of the wall post
|
9
|
+
# post.description # => The content of the wall post
|
10
|
+
# post.pubDate # => The date the post was published
|
11
|
+
# post.author # => The author of the post
|
12
|
+
class Post
|
13
|
+
attr_reader :feed_entry
|
14
|
+
|
15
|
+
def initialize(feed_entry)
|
16
|
+
@feed_entry = feed_entry
|
17
|
+
end
|
18
|
+
|
19
|
+
def method_missing(method_id) #:nodoc:
|
20
|
+
feed_entry.send method_id.id2name
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
require 'facebook_wall/std_lib_ext/rss/parser'
|
@@ -0,0 +1,15 @@
|
|
1
|
+
#TODO Blog post
|
2
|
+
module RSS
|
3
|
+
class Parser
|
4
|
+
#Fetches, and then parses, the RSS feed at the specified URL
|
5
|
+
#=== Parameters
|
6
|
+
#[url - String] The URL of the RSS feed
|
7
|
+
#=== Returns
|
8
|
+
#RSS::Rss
|
9
|
+
def self.fetch_and_parse(url)
|
10
|
+
open url do |http|
|
11
|
+
RSS::Parser.parse http.read
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
require 'test/unit'
|
2
|
+
|
3
|
+
lib = File.expand_path("#{File.dirname(__FILE__)}/../../lib")
|
4
|
+
$:.unshift(lib) unless $:.include?('lib') || $:.include?(lib)
|
5
|
+
|
6
|
+
require 'facebook_wall'
|
7
|
+
|
8
|
+
def create_feed(xml)
|
9
|
+
RSS::Parser.parse xml
|
10
|
+
end
|
11
|
+
|
12
|
+
def create_feed_entry(attributes = {})
|
13
|
+
feed_entry = RSS::Rss::Channel::Item.new
|
14
|
+
|
15
|
+
attributes.each do |name, value|
|
16
|
+
feed_entry.send("#{name}=", value)
|
17
|
+
end
|
18
|
+
|
19
|
+
feed_entry
|
20
|
+
end
|
21
|
+
|
22
|
+
module FacebookWall
|
23
|
+
class Post
|
24
|
+
def eql?(other)
|
25
|
+
#This will hold for as long as the feed entry is the only source of data
|
26
|
+
other.instance_of?(self.class) && feed_entry.to_s.eql?(other.feed_entry.to_s)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def create_post(attributes)
|
32
|
+
FacebookWall::Post.new create_feed_entry(attributes)
|
33
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
require 'abstract_unit'
|
2
|
+
|
3
|
+
class FacebookWallTest < Test::Unit::TestCase
|
4
|
+
def test_feed_url_returns_the_url_of_the_wall_feed_for_the_specified_page
|
5
|
+
assert_equal 'http://www.facebook.com/feeds/page.php?id=260041833284&format=rss20', FacebookWall::feed_url(260041833284)
|
6
|
+
end
|
7
|
+
|
8
|
+
def test_posts_returns_an_array_of_posts
|
9
|
+
posts = FacebookWall::posts_by 260041833284
|
10
|
+
assert posts.instance_of?(Array)
|
11
|
+
assert posts.length > 0
|
12
|
+
assert posts.first.instance_of?(FacebookWall::Post)
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,112 @@
|
|
1
|
+
require 'abstract_unit'
|
2
|
+
|
3
|
+
class FactoriesTest < Test::Unit::TestCase
|
4
|
+
def test_create_feed_entry_filter_chain_creates_a_chain_containing_all_filters_in_the_feed_entry_filters_module
|
5
|
+
feed_entry_filters_module = FacebookWall::FeedEntryFilters
|
6
|
+
|
7
|
+
feed_entry_filter_chain = FacebookWall::Factories::create_feed_entry_filter_chain
|
8
|
+
|
9
|
+
assert feed_entry_filter_chain.instance_of?(feed_entry_filters_module::Chain)
|
10
|
+
assert_equal 2, feed_entry_filter_chain.length
|
11
|
+
|
12
|
+
expected_classes = [feed_entry_filters_module::Paragraphizer, feed_entry_filters_module::LinkRewriter]
|
13
|
+
|
14
|
+
feed_entry_filter_chain.each{|feed_entry_filter| assert expected_classes.include?(feed_entry_filter.class)}
|
15
|
+
end
|
16
|
+
|
17
|
+
def test_create_posts_creates_an_array_containing_posts
|
18
|
+
feed_xml = <<END
|
19
|
+
<?xml version="1.0" encoding="utf-8"?>
|
20
|
+
<rss version="2.0">
|
21
|
+
<channel>
|
22
|
+
<title>Channel title</title>
|
23
|
+
<link>http://example.com/</link>
|
24
|
+
<description>Channel description</description>
|
25
|
+
<item>
|
26
|
+
<title><![CDATA[Item 1 title]]></title>
|
27
|
+
<link>http://example.com/blog_posts/1</link>
|
28
|
+
<description><![CDATA[Item 1 description]]></description>
|
29
|
+
</item>
|
30
|
+
<item>
|
31
|
+
<title><![CDATA[Item 2 title]]></title>
|
32
|
+
<link>http://example.com/blog_posts/2</link>
|
33
|
+
<description><![CDATA[Item 2 description]]></description>
|
34
|
+
</item>
|
35
|
+
</channel>
|
36
|
+
</rss>
|
37
|
+
END
|
38
|
+
|
39
|
+
feed = create_feed feed_xml
|
40
|
+
feed_entry_filter_chain = FacebookWall::FeedEntryFilters::Chain.new
|
41
|
+
posts = FacebookWall::Factories::create_posts feed, feed_entry_filter_chain
|
42
|
+
|
43
|
+
assert posts.instance_of?(Array)
|
44
|
+
assert_equal 2, posts.length
|
45
|
+
posts.each{|post| assert post.instance_of?(FacebookWall::Post)}
|
46
|
+
|
47
|
+
assert posts.first.eql?(create_post('title' => 'Item 1 title', 'link' => 'http://example.com/blog_posts/1', 'description' => 'Item 1 description'))
|
48
|
+
assert posts.last.eql?(create_post('title' => 'Item 2 title', 'link' => 'http://example.com/blog_posts/2', 'description' => 'Item 2 description'))
|
49
|
+
|
50
|
+
feed = create_feed feed_xml
|
51
|
+
feed_entry_filter_chain = FacebookWall::FeedEntryFilters::Chain.new
|
52
|
+
feed_entry_filter_chain << FactoriesTestDoubles::FeedEntryFilter01.new()
|
53
|
+
feed_entry_filter_chain << FactoriesTestDoubles::FeedEntryFilter02.new()
|
54
|
+
posts = FacebookWall::Factories::create_posts feed, feed_entry_filter_chain
|
55
|
+
|
56
|
+
assert posts.instance_of?(Array)
|
57
|
+
assert_equal 2, posts.length
|
58
|
+
posts.each{|post| assert post.instance_of?(FacebookWall::Post)}
|
59
|
+
|
60
|
+
assert posts.first.eql?(create_post('title' => 'Item 1 title', 'link' => 'http://example.com/blog_posts/1', 'description' => 'Item 1 description foo bar'))
|
61
|
+
assert posts.last.eql?(create_post('title' => 'Item 2 title', 'link' => 'http://example.com/blog_posts/2', 'description' => 'Item 2 description foo bar'))
|
62
|
+
|
63
|
+
feed_xml = <<END
|
64
|
+
<?xml version="1.0" encoding="utf-8"?>
|
65
|
+
<rss version="2.0">
|
66
|
+
<channel>
|
67
|
+
<title>Archaic Horizon's Facebook Wall</title>
|
68
|
+
<link>http://www.facebook.com/</link>
|
69
|
+
<description>Archaic Horizon's Facebook Wall</description>
|
70
|
+
<item>
|
71
|
+
<title><![CDATA[ More of Jeremy Morgan's work here: http://jphmorgan.com]]></title>
|
72
|
+
<link>http://www.facebook.com/archaichorizon/posts/262512677162510</link>
|
73
|
+
<description><![CDATA[More of Jeremy Morgan's work here: <a href="http://jphmorgan.com/" target="_blank" rel="nofollow nofollow" onmousedown="UntrustedLink.bootstrap($(this), "4AQEEBrT_", event, bagof({}));">http://jphmorgan.com/</a><br/><br/><a href="http://27.media.tumblr.com/tumblr_m09mfxU8kE1qdrgo9o9_r8_500.jpg" id="" title="" target="" onclick="" style="" onmousedown="UntrustedLink.bootstrap($(this), "PAQGoceG_", event, bagof({}));" rel="nofollow"><img class="img" src="http://external.ak.fbcdn.net/safe_image.php?d=AQDzaFjOqqk40sEl&w=90&h=90&url=http%3A%2F%2F27.media.tumblr.com%2Ftumblr_m09mfxU8kE1qdrgo9o9_r8_500.jpg" alt="" /></a><br/><a href="http://27.media.tumblr.com/tumblr_m09mfxU8kE1qdrgo9o9_r8_500.jpg" id="" target="_blank" style="" onmousedown="UntrustedLink.bootstrap($(this), "eAQE2srCn", event, bagof({}));" rel="nofollow"><a href="http://27.media.tumblr.com/tumblr_m09mfxU8kE1qdrgo9o9_r8_500.jpg" target="_blank" rel="nofollow nofollow" onmousedown="UntrustedLink.bootstrap($(this), "yAQFp-Snl", event, bagof({}));">http://27.media.tumblr.com/tumblr_m09mfxU8kE1qdrgo9o9_r8_500.jpg</a></a><br/>27.media.tumblr.com]]></description>
|
74
|
+
</item>
|
75
|
+
<item>
|
76
|
+
<title><![CDATA[ A generative ambient sound instrument created by Batuhan Bozkurt, a sound artist...]]></title>
|
77
|
+
<link>http://www.facebook.com/archaichorizon/posts/377150222297415</link>
|
78
|
+
<description><![CDATA[A generative ambient sound instrument created by Batuhan Bozkurt, a sound artist, computer programmer, performer and overall a curious person currently living in Istanbul, Turkey.<br /> <br /> <a href="http://www.earslap.com/projectslab/circuli?q=2608v4564v1o_2o66564k1k7_2o4o063442262o" target="_blank" rel="nofollow nofollow" onmousedown="UntrustedLink.bootstrap($(this), "CAQHV5LbD", event, bagof({}));">http://www.earslap.com/projectslab/circuli?q=2608v4564v1o_2o66564k1k7_2o4o063442262o</a><br/><br/><br/><a href="http://www.earslap.com/projectslab/circuli?q=2608v4564v1o_2o66564k1k7_2o4o063442262o" id="" target="_blank" style="" onmousedown="UntrustedLink.bootstrap($(this), "mAQGmvWic", event, bagof({}));" rel="nofollow">Circuli</a><br/>www.earslap.com]]></description>
|
79
|
+
</item>
|
80
|
+
</channel>
|
81
|
+
</rss>
|
82
|
+
END
|
83
|
+
|
84
|
+
feed = create_feed feed_xml
|
85
|
+
feed_entry_filter_chain = FacebookWall::Factories::create_feed_entry_filter_chain
|
86
|
+
posts = FacebookWall::Factories::create_posts feed, feed_entry_filter_chain
|
87
|
+
|
88
|
+
assert_equal(
|
89
|
+
'<p>More of Jeremy Morgan's work here: <a href="http://jphmorgan.com/">http://jphmorgan.com/</a></p><p><a href="http://27.media.tumblr.com/tumblr_m09mfxU8kE1qdrgo9o9_r8_500.jpg"><img class="img" src="http://external.ak.fbcdn.net/safe_image.php?d=AQDzaFjOqqk40sEl&w=90&h=90&url=http%3A%2F%2F27.media.tumblr.com%2Ftumblr_m09mfxU8kE1qdrgo9o9_r8_500.jpg" alt="" /></a><br/><a href="http://27.media.tumblr.com/tumblr_m09mfxU8kE1qdrgo9o9_r8_500.jpg"><a href="http://27.media.tumblr.com/tumblr_m09mfxU8kE1qdrgo9o9_r8_500.jpg">http://27.media.tumblr.com/tumblr_m09mfxU8kE1qdrgo9o9_r8_500.jpg</a></a><br/>27.media.tumblr.com</p>',
|
90
|
+
posts.first.description
|
91
|
+
)
|
92
|
+
|
93
|
+
assert_equal(
|
94
|
+
'<p>A generative ambient sound instrument created by Batuhan Bozkurt, a sound artist, computer programmer, performer and overall a curious person currently living in Istanbul, Turkey.</p><p><a href="http://www.earslap.com/projectslab/circuli?q=2608v4564v1o_2o66564k1k7_2o4o063442262o">http://www.earslap.com/projectslab/circuli?q=2608v4564v1o_2o66564k1k7_2o4o063442262o</a></p><p><a href="http://www.earslap.com/projectslab/circuli?q=2608v4564v1o_2o66564k1k7_2o4o063442262o">Circuli</a><br/>www.earslap.com</p>',
|
95
|
+
posts.last.description
|
96
|
+
)
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
module FactoriesTestDoubles
|
101
|
+
class FeedEntryFilter01 < FacebookWall::FeedEntryFilters::FeedEntryFilter
|
102
|
+
def apply!(feed_entry)
|
103
|
+
feed_entry.description << ' foo'
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
class FeedEntryFilter02 < FacebookWall::FeedEntryFilters::FeedEntryFilter
|
108
|
+
def apply!(feed_entry)
|
109
|
+
feed_entry.description << ' bar'
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
require 'abstract_unit'
|
2
|
+
|
3
|
+
class ChainTest < Test::Unit::TestCase
|
4
|
+
def test_is_an_array
|
5
|
+
assert FacebookWall::FeedEntryFilters::Chain < Array
|
6
|
+
end
|
7
|
+
|
8
|
+
def test_apply_filters_to_applies_the_filters_to_the_specified_feed_entry
|
9
|
+
feed_entry_filter_chain = FacebookWall::FeedEntryFilters::Chain.new
|
10
|
+
feed_entry = create_feed_entry 'description' => 'Description'
|
11
|
+
feed_entry_filter_chain.apply_filters_to! feed_entry
|
12
|
+
|
13
|
+
assert_equal 'Description', feed_entry.description
|
14
|
+
|
15
|
+
feed_entry_filter_chain = FacebookWall::FeedEntryFilters::Chain.new
|
16
|
+
feed_entry_filter_chain << ChainTestDoubles::FeedEntryFilter01.new
|
17
|
+
feed_entry_filter_chain << ChainTestDoubles::FeedEntryFilter02.new
|
18
|
+
feed_entry = create_feed_entry 'description' => 'Description'
|
19
|
+
feed_entry_filter_chain.apply_filters_to! feed_entry
|
20
|
+
|
21
|
+
assert_equal 'Description foo bar', feed_entry.description
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
#TODO Blog post
|
26
|
+
module ChainTestDoubles
|
27
|
+
class FeedEntryFilter01 < FacebookWall::FeedEntryFilters::FeedEntryFilter
|
28
|
+
def apply!(feed_entry)
|
29
|
+
feed_entry.description << ' foo'
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
class FeedEntryFilter02 < FacebookWall::FeedEntryFilters::FeedEntryFilter
|
34
|
+
def apply!(feed_entry)
|
35
|
+
feed_entry.description << ' bar'
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
require 'abstract_unit'
|
2
|
+
|
3
|
+
class LinkRewriterTest < Test::Unit::TestCase
|
4
|
+
def test_is_a_feed_entry_filter
|
5
|
+
assert FacebookWall::FeedEntryFilters::LinkRewriter < FacebookWall::FeedEntryFilters::FeedEntryFilter
|
6
|
+
end
|
7
|
+
|
8
|
+
def test_apply_rewrites_links_in_the_description_in_the_feed_entry
|
9
|
+
expected_description = '<a href="http://example.com">example.com</a>'
|
10
|
+
|
11
|
+
feed_entry = create_feed_entry(
|
12
|
+
'description' => '<a href="http://example.com" target="_blank" onmousedown="doSomething()" rel="nofollow" id="link">example.com</a>'
|
13
|
+
)
|
14
|
+
|
15
|
+
link_rewriter = FacebookWall::FeedEntryFilters::LinkRewriter.new
|
16
|
+
link_rewriter.apply! feed_entry
|
17
|
+
|
18
|
+
assert_equal expected_description, feed_entry.description
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
require 'abstract_unit'
|
2
|
+
|
3
|
+
class ParagraphizerTest < Test::Unit::TestCase
|
4
|
+
def test_is_a_feed_entry_filter
|
5
|
+
assert FacebookWall::FeedEntryFilters::Paragraphizer < FacebookWall::FeedEntryFilters::FeedEntryFilter
|
6
|
+
end
|
7
|
+
|
8
|
+
def test_apply_paragraphizes_the_description_in_the_feed_entry
|
9
|
+
expected_description = '<p>First paragraph</p><p>Second paragraph</p><p>Something inserted<br/>by Facebook</p>'
|
10
|
+
|
11
|
+
rss_entry = create_feed_entry(
|
12
|
+
'description' => 'First paragraph<br /> <br /> Second paragraph<br/><br/><br/>Something inserted<br/>by Facebook'
|
13
|
+
)
|
14
|
+
|
15
|
+
paragraphizer = FacebookWall::FeedEntryFilters::Paragraphizer.new
|
16
|
+
paragraphizer.apply! rss_entry
|
17
|
+
|
18
|
+
assert_equal expected_description, rss_entry.description
|
19
|
+
end
|
20
|
+
end
|
data/test/post_test.rb
ADDED
@@ -0,0 +1,39 @@
|
|
1
|
+
require 'abstract_unit'
|
2
|
+
|
3
|
+
class PostTest < Test::Unit::TestCase
|
4
|
+
def test_is_initialized_with_a_feed_entry
|
5
|
+
feed_entry = create_feed_entry
|
6
|
+
post = FacebookWall::Post.new feed_entry
|
7
|
+
|
8
|
+
assert_same feed_entry, post.feed_entry
|
9
|
+
end
|
10
|
+
|
11
|
+
def test_decorates_the_feed_entry
|
12
|
+
feed_entry = create_feed_entry 'title' => 'Foo', 'description' => 'Bar baz bip'
|
13
|
+
post = FacebookWall::Post.new feed_entry
|
14
|
+
|
15
|
+
assert_same feed_entry.title, post.title
|
16
|
+
assert_same feed_entry.description, post.description
|
17
|
+
end
|
18
|
+
|
19
|
+
#eql?() is added, for testing purposes only, by abstract_unit
|
20
|
+
def test_eql_returns_true_if_the_specified_object_is_a_post_with_the_same_value
|
21
|
+
attributes = {'title' => 'Item 1 title', 'link' => 'http://example.com/blog_posts/1', 'description' => 'Item 1 description'}
|
22
|
+
|
23
|
+
post = FacebookWall::Post.new create_feed_entry(attributes)
|
24
|
+
another_post = FacebookWall::Post.new create_feed_entry(attributes)
|
25
|
+
|
26
|
+
assert post.eql?(another_post)
|
27
|
+
|
28
|
+
almost_a_feed_entry = "<item>\n <title>Item 1 title</title>\n <link>http://example.com/blog_posts/1</link>\n <description>Item 1 description</description>\n</item>"
|
29
|
+
post = FacebookWall::Post.new create_feed_entry(attributes)
|
30
|
+
|
31
|
+
assert_equal almost_a_feed_entry, post.feed_entry.to_s
|
32
|
+
assert ! post.eql?(almost_a_feed_entry)
|
33
|
+
|
34
|
+
not_a_post = 123
|
35
|
+
post = FacebookWall::Post.new create_feed_entry(attributes)
|
36
|
+
|
37
|
+
assert ! post.eql?(not_a_post)
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,9 @@
|
|
1
|
+
require 'abstract_unit'
|
2
|
+
|
3
|
+
class ParserTest < Test::Unit::TestCase
|
4
|
+
def test_fetch_and_parse_fetches_and_parses_the_feed_at_the_specified_url
|
5
|
+
feed = RSS::Parser.fetch_and_parse 'http://www.facebook.com/feeds/page.php?id=260041833284&format=rss20'
|
6
|
+
assert feed.instance_of?(RSS::Rss)
|
7
|
+
assert feed.items.length > 0
|
8
|
+
end
|
9
|
+
end
|
metadata
ADDED
@@ -0,0 +1,84 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: facebook_wall
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.0
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Dan Bettles
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2012-03-20 00:00:00.000000000Z
|
13
|
+
dependencies: []
|
14
|
+
description: facebook_wall is a very simple library that fetches the latest wall-posts
|
15
|
+
for a Facebook page without having to log in - wall posts are taken from the page's
|
16
|
+
RSS feed. The markup in posts is filtered to help ensure standards-compliance and
|
17
|
+
prevent errors when inserted into your own Web pages.
|
18
|
+
email:
|
19
|
+
- dan@archaichorizon.com
|
20
|
+
executables: []
|
21
|
+
extensions: []
|
22
|
+
extra_rdoc_files: []
|
23
|
+
files:
|
24
|
+
- .gitignore
|
25
|
+
- Gemfile
|
26
|
+
- README.rdoc
|
27
|
+
- Rakefile
|
28
|
+
- facebook_wall.gemspec
|
29
|
+
- lib/facebook_wall.rb
|
30
|
+
- lib/facebook_wall/factories/methods.rb
|
31
|
+
- lib/facebook_wall/feed_entry_filters/chain.rb
|
32
|
+
- lib/facebook_wall/feed_entry_filters/feed_entry_filter.rb
|
33
|
+
- lib/facebook_wall/feed_entry_filters/link_rewriter.rb
|
34
|
+
- lib/facebook_wall/feed_entry_filters/paragraphizer.rb
|
35
|
+
- lib/facebook_wall/methods.rb
|
36
|
+
- lib/facebook_wall/post.rb
|
37
|
+
- lib/facebook_wall/std_lib_ext.rb
|
38
|
+
- lib/facebook_wall/std_lib_ext/rss/parser.rb
|
39
|
+
- lib/facebook_wall/version.rb
|
40
|
+
- test/abstract_unit.rb
|
41
|
+
- test/facebook_wall_test.rb
|
42
|
+
- test/factories/factories_test.rb
|
43
|
+
- test/feed_entry_filters/chain_test.rb
|
44
|
+
- test/feed_entry_filters/feed_entry_filter_test.rb
|
45
|
+
- test/feed_entry_filters/link_rewriter_test.rb
|
46
|
+
- test/feed_entry_filters/paragraphizer_test.rb
|
47
|
+
- test/post_test.rb
|
48
|
+
- test/std_lib_ext/rss/parser_test.rb
|
49
|
+
homepage: http://www.archaichorizon.com/
|
50
|
+
licenses: []
|
51
|
+
post_install_message:
|
52
|
+
rdoc_options: []
|
53
|
+
require_paths:
|
54
|
+
- lib
|
55
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
56
|
+
none: false
|
57
|
+
requirements:
|
58
|
+
- - ! '>='
|
59
|
+
- !ruby/object:Gem::Version
|
60
|
+
version: '0'
|
61
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
62
|
+
none: false
|
63
|
+
requirements:
|
64
|
+
- - ! '>='
|
65
|
+
- !ruby/object:Gem::Version
|
66
|
+
version: '0'
|
67
|
+
requirements: []
|
68
|
+
rubyforge_project: facebook_wall
|
69
|
+
rubygems_version: 1.8.15
|
70
|
+
signing_key:
|
71
|
+
specification_version: 3
|
72
|
+
summary: facebook_wall is a very simple library that fetches the latest wall-posts
|
73
|
+
for a Facebook page without having to log in - wall posts are taken from the page's
|
74
|
+
RSS feed.
|
75
|
+
test_files:
|
76
|
+
- test/abstract_unit.rb
|
77
|
+
- test/facebook_wall_test.rb
|
78
|
+
- test/factories/factories_test.rb
|
79
|
+
- test/feed_entry_filters/chain_test.rb
|
80
|
+
- test/feed_entry_filters/feed_entry_filter_test.rb
|
81
|
+
- test/feed_entry_filters/link_rewriter_test.rb
|
82
|
+
- test/feed_entry_filters/paragraphizer_test.rb
|
83
|
+
- test/post_test.rb
|
84
|
+
- test/std_lib_ext/rss/parser_test.rb
|