feedbuilder 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,3 @@
1
+ pkg/*
2
+ *.gem
3
+ .bundle
data/.rspec ADDED
@@ -0,0 +1 @@
1
+ --color --format progress --require spec/spec_helper.rb
data/Gemfile ADDED
@@ -0,0 +1,2 @@
1
+ source "http://rubygems.org"
2
+ gemspec
data/Gemfile.lock ADDED
@@ -0,0 +1,32 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ feedbuilder (0.0.2)
5
+ activesupport (~> 3.0)
6
+ ratom
7
+
8
+ GEM
9
+ remote: http://rubygems.org/
10
+ specs:
11
+ activesupport (3.0.3)
12
+ diff-lcs (1.1.2)
13
+ libxml-ruby (1.1.4)
14
+ ratom (0.6.7)
15
+ libxml-ruby (>= 1.1.2)
16
+ rspec (2.1.0)
17
+ rspec-core (~> 2.1.0)
18
+ rspec-expectations (~> 2.1.0)
19
+ rspec-mocks (~> 2.1.0)
20
+ rspec-core (2.1.0)
21
+ rspec-expectations (2.1.0)
22
+ diff-lcs (~> 1.1.2)
23
+ rspec-mocks (2.1.0)
24
+
25
+ PLATFORMS
26
+ ruby
27
+
28
+ DEPENDENCIES
29
+ activesupport (~> 3.0)
30
+ feedbuilder!
31
+ ratom
32
+ rspec (~> 2.0)
data/LICENSE ADDED
@@ -0,0 +1,13 @@
1
+ Copyright (c) 2009 Brian Moseley
2
+
3
+ Licensed under the Apache License, Version 2.0 (the "License");
4
+ you may not use this file except in compliance with the License.
5
+ You may obtain a copy of the License at
6
+
7
+ http://www.apache.org/licenses/LICENSE-2.0
8
+
9
+ Unless required by applicable law or agreed to in writing, software
10
+ distributed under the License is distributed on an "AS IS" BASIS,
11
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ See the License for the specific language governing permissions and
13
+ limitations under the License.
data/README.md ADDED
@@ -0,0 +1,53 @@
1
+ Feedbuilder is a tiny utility that simplifies the process of building and delivering Atom feeds.
2
+
3
+ ## Usage
4
+
5
+ require 'rubygems'
6
+ require 'bundler/setup'
7
+ require 'feedbuilder'
8
+ require 'spec/nug'
9
+
10
+ nugs = [1, 2, 3].map {|id| Nug.new(id, "Nug #{id}")}
11
+ url_builder = FeedBuilder::UrlBuilder.new('http://test.host/', 'http://test.host/nugs')
12
+ feed = Nug.build_feed(nugs, url_builder, :feed_title => "Bucket o' nugs") do |nug, entry|
13
+ entry.links << Atom::Link.new(:href => "/nugs/#{nug.id}", :rel => :alternate, :type => 'text/html')
14
+ end
15
+
16
+ # feed.to_xml outputs:
17
+
18
+ <?xml version="1.0" encoding="UTF-8"?>
19
+ <feed xmlns="http://www.w3.org/2005/Atom">
20
+ <id>tag:maz.org,2010-12-21:/nugs</id>
21
+ <title>Bucket o' nugs</title>
22
+ <updated>2010-12-21T16:57:21-05:00</updated>
23
+ <link rel="via" type="text/html" href="http://test.host/nugs"/>
24
+ <link rel="self" type="application/atom+xml" href="http://test.host/nugs.atom"/>
25
+ <entry>
26
+ <id>tag:maz.org,2010-12-21:/nugs/1</id>
27
+ <summary>A nug named Nug 1</summary>
28
+ <updated>2010-12-21T16:57:21-05:00</updated>
29
+ <published>2010-12-21T16:57:21-05:00</published>
30
+ <link rel="alternate" type="text/html" href="http://test.host/nugs/1"/>
31
+ <content type="html">&lt;p&gt;A nug named Nug 1&lt;/p&gt;</content>
32
+ </entry>
33
+ <entry>
34
+ <id>tag:maz.org,2010-12-21:/nugs/2</id>
35
+ <summary>A nug named Nug 2</summary>
36
+ <updated>2010-12-21T16:57:21-05:00</updated>
37
+ <published>2010-12-21T16:57:21-05:00</published>
38
+ <link rel="alternate" type="text/html" href="http://test.host/nugs/2"/>
39
+ <content type="html">&lt;p&gt;A nug named Nug 2&lt;/p&gt;</content
40
+ </entry>
41
+ <entry>
42
+ <id>tag:maz.org,2010-12-21:/nugs/3</id>
43
+ <summary>A nug named Nug 3</summary>
44
+ <updated>2010-12-21T16:57:21-05:00</updated>
45
+ <published>2010-12-21T16:57:21-05:00</published>
46
+ <link rel="alternate" type="text/html" href="http://test.host/nugs/3"/>
47
+ <content type="html">&lt;p&gt;A nug named Nug 3&lt;/p&gt;</content>
48
+ </entry>
49
+ </feed>
50
+
51
+ ## Copyright
52
+
53
+ Copyright (c) 2010 Brian Moseley. See LICENSE for details.
data/Rakefile ADDED
@@ -0,0 +1,6 @@
1
+ require 'bundler'
2
+ Bundler::GemHelper.install_tasks
3
+
4
+ require 'rspec/core/rake_task'
5
+ RSpec::Core::RakeTask.new(:spec)
6
+ task :default => :spec
@@ -0,0 +1,26 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "feedbuilder/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "feedbuilder"
7
+ s.version = FeedBuilder::VERSION
8
+ s.platform = Gem::Platform::RUBY
9
+ s.authors = ["Brian Moseley"]
10
+ s.email = ["bcm@maz.org"]
11
+ s.homepage = ""
12
+ s.summary = %q{An easier way to build Atom feeds}
13
+ s.description =
14
+ %q{A utility that simplifies the process of building Atom feeds from collections of well-behaved objects}
15
+
16
+ s.rubyforge_project = "feedbuilder"
17
+
18
+ s.add_dependency 'activesupport', '~> 3.0'
19
+ s.add_dependency 'ratom'
20
+ s.add_development_dependency 'rspec', '~> 2.0'
21
+
22
+ s.files = `git ls-files`.split("\n")
23
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
24
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
25
+ s.require_paths = ["lib"]
26
+ end
@@ -0,0 +1,65 @@
1
+ module FeedBuilder
2
+ module Provider
3
+ mattr_accessor :feed_id_domain
4
+ mattr_accessor :feed_id_date
5
+ mattr_accessor :feed_id_path
6
+ mattr_accessor :feed_id
7
+
8
+ def build_feed(collection, url_builder, options = {}, &block)
9
+ feed_updated = nil
10
+ collection.each do |model|
11
+ if feed_updated.nil? || model.updated_at > feed_updated
12
+ feed_updated = model.updated_at
13
+ end
14
+ end
15
+ Atom::Feed.new do |feed|
16
+ feed.id = if options.include?(:feed_id)
17
+ options[:feed_id]
18
+ elsif options.include?(:feed_id_path)
19
+ feed_tag_uri(options[:feed_id_path])
20
+ elsif self.feed_id_path.present?
21
+ feed_tag_uri(self.feed_id_path)
22
+ else
23
+ self.feed_id
24
+ end
25
+ feed.title = options[:feed_title]
26
+ feed.updated = feed_updated
27
+ feed.links << Atom::Link.new(:href => url_builder.html_url, :rel => :via, :type => 'text/html')
28
+ feed.links << Atom::Link.new(:href => url_builder.self_url, :rel => :self, :type => 'application/atom+xml')
29
+ if collection.respond_to?(:total_pages) && collection.respond_to?(:current_page)
30
+ if collection.total_pages > 1
31
+ feed.links << Atom::Link.new(:href => url_builder.first_url, :rel => :first)
32
+ if collection.current_page > 1
33
+ feed.links << Atom::Link.new(:href => url_builder.prev_url(collection.current_page), :rel => :previous)
34
+ end
35
+ if collection.current_page < collection.total_pages
36
+ feed.links << Atom::Link.new(:href => url_builder.next_url(collection.current_page), :rel => :next)
37
+ end
38
+ feed.links << Atom::Link.new(:href => url_builder.last_url(collection.total_pages), :rel => :last)
39
+ end
40
+ end
41
+ collection.each {|model| feed.entries << build_entry(model, &block)}
42
+ end
43
+ end
44
+
45
+ def build_entry(model, &block)
46
+ Atom::Entry.new do |entry|
47
+ entry.id = model.entry_id if model.respond_to?(:entry_id)
48
+ entry.title = model.entry_title if model.respond_to?(:entry_title)
49
+ entry.published = model.respond_to?(:entry_published) ? model.entry_published : model.created_at
50
+ entry.updated = model.respond_to?(:entry_updated) ? model.entry_updated : model.updated_at
51
+ entry.summary = model.entry_summary if model.respond_to?(:entry_summary)
52
+ entry.content = model.entry_content if model.respond_to?(:entry_content)
53
+ yield(model, entry) if block_given?
54
+ end
55
+ end
56
+
57
+ # as per http://diveintomark.org/archives/2004/05/28/howto-atom-id
58
+ def feed_tag_uri(path, options = {})
59
+ domain = options[:domain] || self.feed_id_domain || FeedBuilder.feed_id_domain
60
+ date = options[:date] || self.feed_id_date || Date.today
61
+ date_str = date.acts_like_date?? date.strftime("%Y-%m-%d") : date.to_s
62
+ "tag:#{domain},#{date_str}:#{path.gsub(/\#/, '/')}"
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,47 @@
1
+ module FeedBuilder
2
+ class UrlBuilder
3
+ attr_reader :base_url, :html_url, :self_url
4
+
5
+ def initialize(base_url, html_url, self_url = nil, options = {})
6
+ @base_url = base_url
7
+ @html_url = html_url
8
+ if self_url.present?
9
+ @self_url = self_url
10
+ else
11
+ @self_url = html_url.dup
12
+ if @self_url =~ /\?/
13
+ @self_url.sub!(/\?/, '.atom?')
14
+ else
15
+ @self_url << '.atom'
16
+ end
17
+ end
18
+ @page_param = options[:page_param] || :page
19
+ @page_size_param = options[:page_size_param] || :page_size
20
+ end
21
+
22
+ def first_url(page_size)
23
+ add_params(self_url, @page_param => 1, @page_size_param => page_size)
24
+ end
25
+
26
+ def next_url(current_page, page_size)
27
+ add_params(self_url, @page_param => current_page + 1, @page_size_param => page_size)
28
+ end
29
+
30
+ def prev_url(current_page, page_size)
31
+ add_params(self_url, @page_param => current_page - 1, @page_size_param => page_size)
32
+ end
33
+
34
+ def last_url(total_pages, page_size)
35
+ add_params(self_url, @page_param => total_pages, @page_size_param => page_size)
36
+ end
37
+
38
+ def add_params(url, params = {})
39
+ unless params.empty?
40
+ qs = params.inject([]) {|m, kv| m << "#{kv[0]}=#{kv[1]}"}
41
+ sep = url =~ /\?/ ? '&' : '?'
42
+ url << "#{sep}#{URI.escape(qs.join('&'))}"
43
+ end
44
+ url
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,3 @@
1
+ module FeedBuilder
2
+ VERSION = '0.0.2'
3
+ end
@@ -0,0 +1,12 @@
1
+ require 'atom'
2
+ require 'active_support/core_ext/date/acts_like'
3
+ require 'active_support/core_ext/date_time/acts_like'
4
+ require 'active_support/core_ext/module/attribute_accessors'
5
+ require 'active_support/core_ext/object/blank'
6
+
7
+ require 'feedbuilder/provider'
8
+ require 'feedbuilder/url_builder'
9
+
10
+ module Feedbuilder
11
+ mattr_accessor :feed_id_domain
12
+ end
data/spec/nug.rb ADDED
@@ -0,0 +1,40 @@
1
+ class Nug
2
+ extend FeedBuilder::Provider
3
+
4
+ # set domain and path here rather than a static feed id so that we can use them to compute entry ids as well
5
+
6
+ self.feed_id_domain = 'maz.org'
7
+ self.feed_id_path = '/nugs'
8
+
9
+ attr_reader :id, :name
10
+
11
+ def initialize(id, name)
12
+ @id = id
13
+ @name = name
14
+ @created_at = DateTime.now
15
+ @updated_at = DateTime.now
16
+ end
17
+
18
+ # created_at and updated_at are used to set the entry's published and updated attributes since entry_published
19
+ # and entry_updated methods aren't defined
20
+
21
+ def created_at
22
+ @created_at
23
+ end
24
+
25
+ def updated_at
26
+ @updated_at
27
+ end
28
+
29
+ def entry_id
30
+ self.class.feed_tag_uri("#{self.class.feed_id_path}/#{self.id}", :date => self.created_at)
31
+ end
32
+
33
+ def entry_summary
34
+ Atom::Content::Text.new("A nug named #{self.name}")
35
+ end
36
+
37
+ def entry_content
38
+ Atom::Content::Html.new("<p>A nug named #{self.name}</p>")
39
+ end
40
+ end
@@ -0,0 +1,115 @@
1
+ describe FeedBuilder::Provider do
2
+ describe "#build_feed" do
3
+ it "builds a feed" do
4
+ feed.should_not be_nil
5
+ end
6
+
7
+ it "sets id to the global feed id" do
8
+ feed_id_path = Nug.feed_id_path
9
+ Nug.feed_id_path = nil
10
+ Nug.feed_id = 'foobar'
11
+ feed(:feed_id => nil).id.should == Nug.feed_id
12
+ Nug.feed_id = nil
13
+ Nug.feed_id_path = feed_id_path
14
+ end
15
+
16
+ it "sets id as provided" do
17
+ id = "foo.com,1999-12-31/nugs"
18
+ feed(:feed_id => id).id.should == id
19
+ end
20
+
21
+ it "sets id as per the provided path" do
22
+ path = '/path/to/nugs'
23
+ feed(:feed_id => nil, :feed_id_path => path).id.should == Nug.feed_tag_uri(path)
24
+ end
25
+
26
+ it "sets title as provided" do
27
+ feed.title.should == @title
28
+ end
29
+
30
+ it "sets updated as provided" do
31
+ feed.updated.should == @nugs.last.updated_at
32
+ end
33
+
34
+ it "adds the correct number of links" do
35
+ feed.links.should have_exactly(2).links
36
+ end
37
+
38
+ it "adds via link" do
39
+ via_link.should_not be_nil
40
+ end
41
+
42
+ it "sets via link to text/html" do
43
+ via_link.type.should == 'text/html'
44
+ end
45
+
46
+ it "sets self link" do
47
+ self_link.should_not be_nil
48
+ end
49
+
50
+ it "sets self link to application/atom+xml" do
51
+ self_link.type.should == 'application/atom+xml'
52
+ end
53
+
54
+ it "adds the correct number of entries" do
55
+ feed.should have_exactly(@nugs.size).entries
56
+ end
57
+ end
58
+
59
+ describe "#build_entry" do
60
+ it "builds an entry" do
61
+ entry.should_not be_nil
62
+ end
63
+
64
+ it "sets id to Nug#entry_id" do
65
+ entry.id.should == @nug.entry_id
66
+ end
67
+
68
+ it "sets title to Nug#name" do
69
+ entry.title.should == @nug.name
70
+ end
71
+
72
+ it "sets published to Nug#created_at" do
73
+ entry.published.should == @nug.created_at
74
+ end
75
+
76
+ it "sets updated to Nug#updated_at" do
77
+ entry.updated.should == @nug.updated_at
78
+ end
79
+
80
+ it "sets text summary" do
81
+ entry.summary.should be_a(Atom::Content::Text)
82
+ end
83
+
84
+ it "sets html content" do
85
+ entry.content.should be_a(Atom::Content::Html)
86
+ end
87
+ end
88
+ end
89
+
90
+ def feed(options = {})
91
+ @title = "Bucket o' nugs"
92
+ @nugs = [1, 2, 3].map {|id| Nug.new(id, "Nug #{id}")}
93
+ options = {:feed_title => @title}.merge(options)
94
+ if options.include?(:feed_id)
95
+ options.delete(:feed_id) if options[:feed_id].nil?
96
+ else
97
+ options[:feed_id] = Nug.feed_tag_uri(Nug.feed_id_path)
98
+ end
99
+ Nug.build_feed(@nugs, FeedBuilder::UrlBuilder.new('/', '/nugs'), options)
100
+ end
101
+
102
+ def entry
103
+ @nug = Nug.new(1, "Fug slug")
104
+ Nug.build_entry(@nug) do |nug, entry|
105
+ entry.title = nug.name
106
+ end
107
+ end
108
+
109
+ def via_link
110
+ feed.links.detect {|l| l.rel == :via}
111
+ end
112
+
113
+ def self_link
114
+ feed.links.detect {|l| l.rel == :self}
115
+ end
@@ -0,0 +1,5 @@
1
+ require 'rubygems'
2
+ require 'bundler/setup'
3
+
4
+ require 'lib/feedbuilder'
5
+ require 'nug'
metadata ADDED
@@ -0,0 +1,127 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: feedbuilder
3
+ version: !ruby/object:Gem::Version
4
+ hash: 27
5
+ prerelease: false
6
+ segments:
7
+ - 0
8
+ - 0
9
+ - 2
10
+ version: 0.0.2
11
+ platform: ruby
12
+ authors:
13
+ - Brian Moseley
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2010-12-21 00:00:00 -05:00
19
+ default_executable:
20
+ dependencies:
21
+ - !ruby/object:Gem::Dependency
22
+ name: activesupport
23
+ prerelease: false
24
+ requirement: &id001 !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ~>
28
+ - !ruby/object:Gem::Version
29
+ hash: 7
30
+ segments:
31
+ - 3
32
+ - 0
33
+ version: "3.0"
34
+ type: :runtime
35
+ version_requirements: *id001
36
+ - !ruby/object:Gem::Dependency
37
+ name: ratom
38
+ prerelease: false
39
+ requirement: &id002 !ruby/object:Gem::Requirement
40
+ none: false
41
+ requirements:
42
+ - - ">="
43
+ - !ruby/object:Gem::Version
44
+ hash: 3
45
+ segments:
46
+ - 0
47
+ version: "0"
48
+ type: :runtime
49
+ version_requirements: *id002
50
+ - !ruby/object:Gem::Dependency
51
+ name: rspec
52
+ prerelease: false
53
+ requirement: &id003 !ruby/object:Gem::Requirement
54
+ none: false
55
+ requirements:
56
+ - - ~>
57
+ - !ruby/object:Gem::Version
58
+ hash: 3
59
+ segments:
60
+ - 2
61
+ - 0
62
+ version: "2.0"
63
+ type: :development
64
+ version_requirements: *id003
65
+ description: A utility that simplifies the process of building Atom feeds from collections of well-behaved objects
66
+ email:
67
+ - bcm@maz.org
68
+ executables: []
69
+
70
+ extensions: []
71
+
72
+ extra_rdoc_files: []
73
+
74
+ files:
75
+ - .gitignore
76
+ - .rspec
77
+ - Gemfile
78
+ - Gemfile.lock
79
+ - LICENSE
80
+ - README.md
81
+ - Rakefile
82
+ - feedbuilder.gemspec
83
+ - lib/feedbuilder.rb
84
+ - lib/feedbuilder/provider.rb
85
+ - lib/feedbuilder/url_builder.rb
86
+ - lib/feedbuilder/version.rb
87
+ - spec/nug.rb
88
+ - spec/provider_spec.rb
89
+ - spec/spec_helper.rb
90
+ has_rdoc: true
91
+ homepage: ""
92
+ licenses: []
93
+
94
+ post_install_message:
95
+ rdoc_options: []
96
+
97
+ require_paths:
98
+ - lib
99
+ required_ruby_version: !ruby/object:Gem::Requirement
100
+ none: false
101
+ requirements:
102
+ - - ">="
103
+ - !ruby/object:Gem::Version
104
+ hash: 3
105
+ segments:
106
+ - 0
107
+ version: "0"
108
+ required_rubygems_version: !ruby/object:Gem::Requirement
109
+ none: false
110
+ requirements:
111
+ - - ">="
112
+ - !ruby/object:Gem::Version
113
+ hash: 3
114
+ segments:
115
+ - 0
116
+ version: "0"
117
+ requirements: []
118
+
119
+ rubyforge_project: feedbuilder
120
+ rubygems_version: 1.3.7
121
+ signing_key:
122
+ specification_version: 3
123
+ summary: An easier way to build Atom feeds
124
+ test_files:
125
+ - spec/nug.rb
126
+ - spec/provider_spec.rb
127
+ - spec/spec_helper.rb