facebook_wall 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.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
|