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.
@@ -0,0 +1,7 @@
1
+ *.gem
2
+ .bundle
3
+ Gemfile.lock
4
+ pkg/*
5
+
6
+ .DS_Store
7
+ doc/
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in facebook_wall.gemspec
4
+ gemspec
@@ -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
@@ -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,3 @@
1
+ module FacebookWall
2
+ VERSION = "1.0.0"
3
+ 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&apos;s Facebook Wall</title>
68
+ <link>http://www.facebook.com/</link>
69
+ <description>Archaic Horizon&apos;s Facebook Wall</description>
70
+ <item>
71
+ <title><![CDATA[ More of Jeremy Morgan&#039;s work here: http://jphmorgan.com]]></title>
72
+ <link>http://www.facebook.com/archaichorizon/posts/262512677162510</link>
73
+ <description><![CDATA[More of Jeremy Morgan&#039;s work here: <a href="http://jphmorgan.com/" target="_blank" rel="nofollow nofollow" onmousedown="UntrustedLink.bootstrap($(this), &quot;4AQEEBrT_&quot;, event, bagof(&#123;&#125;));">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), &quot;PAQGoceG_&quot;, event, bagof(&#123;&#125;));" rel="nofollow"><img class="img" src="http://external.ak.fbcdn.net/safe_image.php?d=AQDzaFjOqqk40sEl&amp;w=90&amp;h=90&amp;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), &quot;eAQE2srCn&quot;, event, bagof(&#123;&#125;));" rel="nofollow"><a href="http://27.media.tumblr.com/tumblr_m09mfxU8kE1qdrgo9o9_r8_500.jpg" target="_blank" rel="nofollow nofollow" onmousedown="UntrustedLink.bootstrap($(this), &quot;yAQFp-Snl&quot;, event, bagof(&#123;&#125;));">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), &quot;CAQHV5LbD&quot;, event, bagof(&#123;&#125;));">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), &quot;mAQGmvWic&quot;, event, bagof(&#123;&#125;));" 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&#039;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&amp;w=90&amp;h=90&amp;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,7 @@
1
+ require 'abstract_unit'
2
+
3
+ class FeedEntryFilterTest < Test::Unit::TestCase
4
+ def test_has_an_instance_method_called_apply
5
+ assert FacebookWall::FeedEntryFilters::FeedEntryFilter.new.respond_to?(:apply!)
6
+ end
7
+ 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
@@ -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