feedbuilder 0.0.2

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 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