pilu-terror 0.2.1

Sign up to get free protection for your applications and to get access to all the features.
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2009 Andrea Franz
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,31 @@
1
+ Terror
2
+ ===
3
+
4
+ A micro feed aggregator based on [Sinatra](http://www.sinatrarb.com/)
5
+
6
+ Installation
7
+ ---
8
+
9
+ gem sources -a http://gems.github.com
10
+ sudo gem install pilu-terror
11
+
12
+ Creating a new aggregator
13
+ ---
14
+
15
+ terror new_aggregator_name
16
+ cd new_aggregator_name
17
+ thin start -C config/thin.yml
18
+
19
+ Edit the config/terror.yml file adding your feeds and database preferences.
20
+
21
+ Fetching feeds
22
+ ---
23
+
24
+ rake feeds:fetch
25
+
26
+ Enjoy
27
+
28
+ Copyright
29
+ ---
30
+
31
+ Copyright (c) 2009 [Andrea Franz](http://gravityblast.com). See LICENSE for details.
data/Rakefile ADDED
@@ -0,0 +1,50 @@
1
+ require 'rubygems'
2
+ require 'rake'
3
+ require File.dirname(__FILE__) + '/lib/terror'
4
+ Dir["#{Terror.root}/lib/**/*.rake"].each{|ext| load ext}
5
+
6
+ set :root, File.join(Terror.root)
7
+
8
+ begin
9
+ require 'jeweler'
10
+ Jeweler::Tasks.new do |gem|
11
+ files = `git ls-files`.split("\n").reject {|f| File.basename(f) =~ /^\.git/ }
12
+
13
+ gem.name = 'terror'
14
+ gem.summary = 'Terror the micro feed aggregator'
15
+ gem.email = "andrea@gravityblast.com"
16
+ gem.homepage = "http://gravityblast.com/projects/terror/"
17
+ gem.authors = ["Andrea Franz"]
18
+ gem.files = files
19
+ gem.executables = ['terror']
20
+ gem.post_install_message = 'Run terror projectname and start aggregating.'
21
+
22
+ gem.add_dependency 'sinatra', ['>= 0.9.1.1']
23
+ gem.add_dependency 'feed-normalizer', ['>= 1.5.1']
24
+ gem.add_dependency 'activerecord', ['>= 2.2.2']
25
+ end
26
+ rescue LoadError
27
+ end
28
+
29
+ require 'rake/testtask'
30
+ Rake::TestTask.new(:test) do |test|
31
+ test.libs << 'lib' << 'test'
32
+ test.pattern = 'test/**/*_test.rb'
33
+ test.verbose = false
34
+ end
35
+
36
+ begin
37
+ require 'rcov/rcovtask'
38
+ Rcov::RcovTask.new do |test|
39
+ test.libs << 'test'
40
+ test.pattern = 'test/**/*_test.rb'
41
+ test.verbose = true
42
+ end
43
+ rescue LoadError
44
+ task :rcov do
45
+ abort "RCov is not available. In order to run rcov, you must: sudo gem install spicycode-rcov"
46
+ end
47
+ end
48
+
49
+
50
+ task :default => :test
data/VERSION.yml ADDED
@@ -0,0 +1,4 @@
1
+ ---
2
+ :patch: 1
3
+ :major: 0
4
+ :minor: 2
data/bin/terror ADDED
@@ -0,0 +1,11 @@
1
+ #!/usr/bin/env ruby
2
+ require File.dirname(__FILE__) + '/../lib/terror/installer'
3
+
4
+ Signal.trap("INT") { puts; exit }
5
+
6
+ if ARGV.size == 1
7
+ Terror::Installer.new(ARGV.first).start
8
+ else
9
+ puts "Usage: terror project_name"
10
+ end
11
+
@@ -0,0 +1,5 @@
1
+ require 'rubygems'
2
+ require 'terror'
3
+ Dir["#{Terror.root}/lib/**/*.rake"].each{|ext| load ext}
4
+
5
+ set :root, File.expand_path('.')
@@ -0,0 +1,16 @@
1
+ #!/usr/bin/env ruby
2
+ require 'rubygems'
3
+ require 'sinatra'
4
+ gem 'terror', '= 0.2.1'
5
+ require 'terror'
6
+
7
+ require File.join(Terror.root, 'terror_aggregator')
8
+
9
+ root_dir = File.dirname(__FILE__)
10
+ set :environment, :production
11
+ set :root, root_dir
12
+ set :public, root_dir + '/public'
13
+ set :views, File.join(Terror.root, 'views')
14
+ disable :run
15
+
16
+ run Sinatra::Application
@@ -0,0 +1,17 @@
1
+ global:
2
+ title: Terror
3
+ subtitle: the micro aggregator
4
+ database:
5
+ development:
6
+ adapter: sqlite3
7
+ database: db/terror_dev.sqlite3
8
+ test:
9
+ adapter: sqlite3
10
+ database: ':memory:'
11
+ production:
12
+ adapter: sqlite3
13
+ database: db/terror.sqlite3
14
+ feeds:
15
+ - http://example.com/rss
16
+ - http://example.com/rss2
17
+ - http://example.com/atom
@@ -0,0 +1,11 @@
1
+ environment: production
2
+ chdir: <%= root %>
3
+ address: 127.0.0.1
4
+ port: 9000
5
+ pid: <%= root %>/tmp/thin.pid
6
+ rackup: <%= root %>/config.ru
7
+ log: <%= root %>/log/thin.log
8
+ max_conns: 1024
9
+ timeout: 30
10
+ max_persistent_conns: 512
11
+ daemonize: true
@@ -0,0 +1,9 @@
1
+ namespace :feeds do
2
+ desc 'Fetch all feeds'
3
+ task :fetch do
4
+ Terror.init
5
+ Terror.config['feeds'].each do |url|
6
+ Terror::Post.fetch_all(url)
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,29 @@
1
+ require 'rubygems'
2
+ require 'feed-normalizer'
3
+ require 'open-uri'
4
+
5
+ module Terror
6
+ module FeedFetcher
7
+
8
+ def self.included(base)
9
+ base.extend ClassMethods
10
+ end
11
+
12
+ module ClassMethods
13
+ def fetch_all(url)
14
+ feed = FeedNormalizer::FeedNormalizer.parse(open(url))
15
+ feed.entries.each{|entry| create_from_entry(feed, entry)}
16
+ end
17
+
18
+ def create_from_entry(feed, entry)
19
+ self.create(
20
+ :title => entry.title,
21
+ :url => entry.url,
22
+ :source => feed.title,
23
+ :date => entry.date_published
24
+ ) unless Post.exists?(:url => entry.url)
25
+ end
26
+ end
27
+
28
+ end
29
+ end
@@ -0,0 +1,35 @@
1
+ module Terror
2
+ module Helper
3
+ def config
4
+ Terror.config
5
+ end
6
+
7
+ def each_post
8
+ @posts.each do |post|
9
+ date = Date.parse(post.date.to_s)
10
+ yield(post, @current_date != date ? @current_date = date : nil)
11
+ end
12
+ end
13
+
14
+ def rfc_3339(time)
15
+ time.strftime("%Y-%m-%dT%H:%M:%SZ")
16
+ end
17
+
18
+ def newer_page?
19
+ @posts.current_page > 1
20
+ end
21
+
22
+ def newer_page
23
+ @posts.current_page - 1
24
+ end
25
+
26
+ def older_page?
27
+ @posts.total_pages > @posts.current_page
28
+ end
29
+
30
+ def older_page
31
+ @posts.current_page + 1
32
+ end
33
+
34
+ end
35
+ end
@@ -0,0 +1,47 @@
1
+ require File.dirname(__FILE__) + '/../terror'
2
+ require 'erb'
3
+
4
+ module Terror
5
+ class Installer
6
+ include FileUtils
7
+
8
+ attr_reader :root
9
+
10
+ def initialize(path)
11
+ @project_name = path
12
+ @root = File.expand_path(path)
13
+ end
14
+
15
+ def start
16
+ create_folders
17
+ copy_files
18
+ create_thin_configuration
19
+ end
20
+
21
+ private
22
+
23
+ def create_folders
24
+ mkdir_p "#{root}/public"
25
+ mkdir_p "#{root}/config"
26
+ mkdir_p "#{root}/db"
27
+ mkdir_p "#{root}/tmp"
28
+ mkdir_p "#{root}/log"
29
+ end
30
+
31
+ def copy_files
32
+ cp "#{Terror.root}/public/style.css", "#{root}/public/style.css"
33
+ cp "#{Terror.root}/config/terror.yml.default", "#{root}/config/terror.yml"
34
+ cp "#{Terror.root}/config/config.ru.default", "#{root}/config.ru"
35
+ cp "#{Terror.root}/config/Rakefile.default", "#{root}/Rakefile"
36
+ end
37
+
38
+ def create_thin_configuration
39
+ File.open(Terror.root + '/config/thin.yml.default') do |template|
40
+ File.open("#{root}/config/thin.yml", 'w') do |output|
41
+ output.write(ERB.new(template.read).result(binding))
42
+ end
43
+ end
44
+ end
45
+
46
+ end
47
+ end
@@ -0,0 +1,27 @@
1
+ require 'feed_fetcher'
2
+
3
+ module Terror
4
+ class Post < ActiveRecord::Base
5
+ include FeedFetcher
6
+
7
+ validates_presence_of :title, :url
8
+ validates_format_of :url, :with => /^http|https:\/\//
9
+
10
+ def self.paginate(options = {})
11
+ per_page = options[:per_page]
12
+ page = options[:page].to_i > 0 ? options[:page].to_i : 1
13
+ posts = self.find(:all, :order => 'date DESC', :limit => per_page, :offset => per_page * (page - 1))
14
+ add_pagination_info(posts, per_page, page)
15
+ end
16
+
17
+ protected
18
+
19
+ def self.add_pagination_info(posts, per_page, page)
20
+ posts.class_eval { attr_accessor :current_page, :total_pages }
21
+ posts.total_pages = (self.count.to_f / per_page).ceil
22
+ posts.current_page = page
23
+ posts
24
+ end
25
+
26
+ end
27
+ end
data/lib/terror.rb ADDED
@@ -0,0 +1,49 @@
1
+ $LOAD_PATH << File.dirname(__FILE__) + '/terror'
2
+ require 'rubygems'
3
+ require 'activerecord'
4
+ require 'sinatra'
5
+ require 'yaml'
6
+ require 'post'
7
+ require 'helper'
8
+
9
+ module Terror
10
+
11
+ class << self
12
+ attr_accessor :config
13
+ end
14
+
15
+ def self.root
16
+ File.expand_path(File.dirname(__FILE__) + '/..')
17
+ end
18
+
19
+ def self.init(config = nil)
20
+ self.config = config.nil? ? YAML.load_file(File.join(Sinatra::Application.root, 'config', 'terror.yml')) : config
21
+ self.init_database
22
+ end
23
+
24
+ def self.init_database
25
+ init_database_connection
26
+ init_database_tables
27
+ end
28
+
29
+ def self.init_database_connection
30
+ ActiveRecord::Base.establish_connection(self.config['database'][Sinatra::Application.environment.to_s])
31
+ end
32
+
33
+ def self.init_database_tables
34
+ ActiveRecord::Schema.define do
35
+ create_table :posts do |t|
36
+ t.string :title
37
+ t.string :source
38
+ t.string :url
39
+ t.datetime :date
40
+ t.datetime :created_at
41
+ end
42
+ add_index :posts, :url
43
+ add_index :posts, :date
44
+ end
45
+ rescue ActiveRecord::StatementInvalid
46
+ # tables already exist
47
+ end
48
+
49
+ end
data/public/style.css ADDED
@@ -0,0 +1,141 @@
1
+ /* reset */
2
+ html, body, div, span, applet, object, iframe,
3
+ h1, h2, h3, h4, h5, h6, p, blockquote, pre,
4
+ a, abbr, acronym, address, big, cite, code,
5
+ del, dfn, em, font, img, ins, kbd, q, s, samp,
6
+ small, strike, strong, sub, sup, tt, var,
7
+ dl, dt, dd, ol, ul, li,
8
+ fieldset, form, label, legend,
9
+ table, caption, tbody, tfoot, thead, tr, th, td {
10
+ margin: 0;
11
+ padding: 0;
12
+ border: 0;
13
+ outline: 0;
14
+ font-weight: inherit;
15
+ font-style: inherit;
16
+ font-size: 100%;
17
+ font-family: inherit;
18
+ vertical-align: baseline;
19
+ }
20
+ /* remember to define focus styles! */
21
+ :focus {
22
+ outline: 0;
23
+ }
24
+ body {
25
+ line-height: 1;
26
+ color: black;
27
+ background: white;
28
+ }
29
+ ol, ul {
30
+ list-style: none;
31
+ }
32
+ /* tables still need 'cellspacing="0"' in the markup */
33
+ table {
34
+ border-collapse: separate;
35
+ border-spacing: 0;
36
+ }
37
+ caption, th, td {
38
+ text-align: left;
39
+ font-weight: normal;
40
+ }
41
+ blockquote:before, blockquote:after,
42
+ q:before, q:after {
43
+ content: "";
44
+ }
45
+ blockquote, q {
46
+ quotes: "" "";
47
+ }
48
+ /* end reset */
49
+
50
+ body {
51
+ text-align: center;
52
+ background: #EEE;
53
+ color: #333;
54
+ font-family: verdana,sans-serif;
55
+ font-size: 12px;
56
+ line-height: 1.4em;
57
+ margin: 0;
58
+ font-family: helvetica,arial,sans-serif;
59
+ }
60
+
61
+ #container {
62
+ text-align: left;
63
+ width: 460px;
64
+ margin: 0 auto;
65
+ }
66
+
67
+ #wrapper {
68
+ background: #FFF;
69
+ padding: 10px 20px;
70
+ border-left: 2px solid #009D57;
71
+ border-right: 2px solid #009D57;
72
+ border-bottom: 2px solid #009D57;
73
+ }
74
+
75
+ #header {
76
+ padding: 10px 0;
77
+ text-align: center;
78
+ }
79
+
80
+ #header h1 a:link, #header h1 a:visited, #header h1 a:hover, #header h1 a:active {
81
+ font-size: 35px;
82
+ color: #009D57;
83
+ text-decoration: none;
84
+ }
85
+
86
+ #header p.subtitle {
87
+ font-size: 15px;
88
+ font-style: italic;
89
+ color: #2BAA9C;
90
+ }
91
+
92
+ .post p.date {
93
+ text-align: right;
94
+ border-bottom: 1px solid #EEE;
95
+ font-size: 13px;
96
+ color: #2BAA9C;
97
+ padding-bottom: 1px;
98
+ margin-bottom: 10px;
99
+ font-style: italic;
100
+ }
101
+
102
+ .post p.source {
103
+ color: #2BAA9C;
104
+ font-style: italic;
105
+ }
106
+
107
+ .post {
108
+ padding: 10px 0;
109
+ }
110
+
111
+ .post h2 {
112
+ font-size: 20px;
113
+ }
114
+
115
+ .post h2 a:link, .post h2 a:visited, .post h2 a:hover, .post h2 a:active {
116
+ color: #009D57;
117
+ text-decoration: none;
118
+ }
119
+
120
+ .pagination {
121
+ padding: 10px 0;
122
+ text-align: center;
123
+ }
124
+
125
+ .pagination a:link, .pagination a:visited, .pagination a:hover, .pagination a:active {
126
+ padding: 2px 5px;
127
+ border: 1px solid #2BAA9C;
128
+ text-decoration: none;
129
+ color: #2BAA9C;
130
+ }
131
+
132
+ #footer {
133
+ color: #AAA;
134
+ text-align: right;
135
+ font-size: 11px;
136
+ padding: 5px 0;
137
+ }
138
+
139
+ #footer a:link, #footer a:visited, #footer a:hover, #footer a:active {
140
+ color: #AAA;
141
+ }
data/terror.gemspec ADDED
@@ -0,0 +1,41 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ Gem::Specification.new do |s|
4
+ s.name = %q{terror}
5
+ s.version = "0.2.1"
6
+
7
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
8
+ s.authors = ["Andrea Franz"]
9
+ s.date = %q{2009-03-16}
10
+ s.default_executable = %q{terror}
11
+ s.email = %q{andrea@gravityblast.com}
12
+ s.executables = ["terror"]
13
+ s.extra_rdoc_files = ["README.md", "LICENSE"]
14
+ s.files = ["LICENSE", "README.md", "Rakefile", "VERSION.yml", "bin/terror", "config/Rakefile.default", "config/config.ru.default", "config/terror.yml.default", "config/thin.yml.default", "lib/tasks/feeds.rake", "lib/terror.rb", "lib/terror/feed_fetcher.rb", "lib/terror/helper.rb", "lib/terror/installer.rb", "lib/terror/post.rb", "public/style.css", "terror.gemspec", "terror_aggregator.rb", "test/feeds/example.xml", "test/post_test.rb", "test/spec_helper.rb", "test/terror_test.rb", "views/feed.builder", "views/index.erb", "views/layout.erb"]
15
+ s.has_rdoc = true
16
+ s.homepage = %q{http://gravityblast.com/projects/terror/}
17
+ s.post_install_message = %q{Run terror projectname and start aggregating.}
18
+ s.rdoc_options = ["--inline-source", "--charset=UTF-8"]
19
+ s.require_paths = ["lib"]
20
+ s.rubygems_version = %q{1.3.1}
21
+ s.summary = %q{Terror the micro feed aggregator}
22
+
23
+ if s.respond_to? :specification_version then
24
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
25
+ s.specification_version = 2
26
+
27
+ if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
28
+ s.add_runtime_dependency(%q<sinatra>, [">= 0.9.1.1"])
29
+ s.add_runtime_dependency(%q<feed-normalizer>, [">= 1.5.1"])
30
+ s.add_runtime_dependency(%q<activerecord>, [">= 2.2.2"])
31
+ else
32
+ s.add_dependency(%q<sinatra>, [">= 0.9.1.1"])
33
+ s.add_dependency(%q<feed-normalizer>, [">= 1.5.1"])
34
+ s.add_dependency(%q<activerecord>, [">= 2.2.2"])
35
+ end
36
+ else
37
+ s.add_dependency(%q<sinatra>, [">= 0.9.1.1"])
38
+ s.add_dependency(%q<feed-normalizer>, [">= 1.5.1"])
39
+ s.add_dependency(%q<activerecord>, [">= 2.2.2"])
40
+ end
41
+ end
@@ -0,0 +1,23 @@
1
+ $LOAD_PATH << File.join(Dir.getwd, 'lib')
2
+ require 'rubygems'
3
+ require 'sinatra'
4
+ require 'terror'
5
+ require 'date'
6
+
7
+ configure { Terror.init }
8
+ helpers { include Terror::Helper }
9
+
10
+ set :root, File.dirname(__FILE__)
11
+
12
+ before do
13
+ @posts = Terror::Post.paginate(:per_page => 20, :page => params[:page])
14
+ end
15
+
16
+ get '/' do
17
+ erb :index, :layout => !request.xhr?
18
+ end
19
+
20
+ get '/feed' do
21
+ content_type :atom
22
+ builder :feed
23
+ end
@@ -0,0 +1,28 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <feed xmlns="http://www.w3.org/2005/Atom" xml-lang="en-US">
3
+ <title>Example</title>
4
+ <id>http://example.com</id>
5
+ <link type="text/html" rel="alternate" href="http://localhost:4567"/>
6
+ <link type="application/atom+xml" href="http://localhost:4567"/>
7
+ <updated>2009-03-15T01:11:33Z</updated>
8
+ <entry>
9
+ <id>http://example.com/post-1</id>
10
+ <link type="text/html" rel="alternate" href="http://example.com/post-1"/>
11
+ <updated>2009-03-15T01:11:33Z</updated>
12
+ <title>Title 1</title>
13
+ <author>
14
+ <name>author</name>
15
+ </author>
16
+ <content>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</content>
17
+ </entry>
18
+ <entry>
19
+ <id>http://example.com/post-2</id>
20
+ <link type="text/html" rel="alternate" href="http://example.com/post-2"/>
21
+ <updated>2009-03-15T01:11:33Z</updated>
22
+ <title>Title 2</title>
23
+ <author>
24
+ <name>author</name>
25
+ </author>
26
+ <content>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</content>
27
+ </entry>
28
+ </feed>
data/test/post_test.rb ADDED
@@ -0,0 +1,39 @@
1
+ require File.dirname(__FILE__) + '/spec_helper'
2
+
3
+ describe 'Creating posts' do
4
+ include Sinatra::Test
5
+
6
+ before do
7
+ @blank_post = Terror::Post.create
8
+ end
9
+
10
+ it 'should not be valid' do
11
+ @blank_post.should_not be_valid
12
+ end
13
+
14
+ it 'should validate presence of title' do
15
+ @blank_post.errors.on(:title).should_not be_nil
16
+ end
17
+
18
+ it 'should validate presence of url' do
19
+ @blank_post.errors.on(:url).should_not be_nil
20
+ end
21
+
22
+ it 'should validate format of url' do
23
+ post = Terror::Post.create(:url => 'example.com')
24
+ post.errors.on(:url).should_not be_nil
25
+ end
26
+
27
+ it 'should create post' do
28
+ post = Terror::Post.create(:url => 'http://example.com/', :title => 'Example')
29
+ post.should be_valid
30
+ end
31
+ end
32
+
33
+ describe 'Fetching feed' do
34
+ it 'should fetch feed and create posts' do
35
+ post_count = Terror::Post.count
36
+ Terror::Post.fetch_all(File.dirname(__FILE__) + '/feeds/example.xml')
37
+ Terror::Post.count.should == post_count + 2
38
+ end
39
+ end
@@ -0,0 +1,6 @@
1
+ require 'rubygems'
2
+ require 'sinatra'
3
+ require 'sinatra/test'
4
+ require 'spec'
5
+ require 'spec/interop/test'
6
+ require File.dirname(__FILE__) + '/../terror_aggregator'
@@ -0,0 +1,17 @@
1
+ require File.dirname(__FILE__) + '/spec_helper'
2
+
3
+ describe 'Test' do
4
+ include Sinatra::Test
5
+
6
+ it 'should show posts' do
7
+ get '/'
8
+ response.should be_ok
9
+ end
10
+
11
+ it 'should show feed' do
12
+ get '/feed'
13
+ response.should be_ok
14
+ response.content_type.should == 'application/atom+xml'
15
+ end
16
+
17
+ end
@@ -0,0 +1,27 @@
1
+ builder do |xml|
2
+ xml.instruct! :xml, :version => '1.0'
3
+ xml.feed :'xml-lang' => 'en-US', :xmlns => 'http://www.w3.org/2005/Atom' do
4
+ xml.title Terror.config['global']['title']
5
+ xml.id 'http://localhost:4567'
6
+ xml.link :type => 'text/html',
7
+ :href => 'http://localhost:4567',
8
+ :rel => 'alternate'
9
+ xml.link :type => 'application/atom+xml',
10
+ :href => 'http://localhost:4567'
11
+ xml.updated(rfc_3339(@posts.first ? @posts.first.created_at : Time.now))
12
+ @posts.each do |post|
13
+ xml.entry do |entry|
14
+ entry.id post.url
15
+ entry.link :type => 'text/html',
16
+ :href => post.url,
17
+ :rel => 'alternate'
18
+ entry.updated rfc_3339(post.created_at)
19
+ entry.title post.title
20
+ entry.author do |author|
21
+ author.name post.source
22
+ end
23
+ entry.content %|<a href="#{post.url}" title="#{post.title}">#{post.url}</a>|, :type => :html
24
+ end
25
+ end
26
+ end
27
+ end
data/views/index.erb ADDED
@@ -0,0 +1,13 @@
1
+ <% unless @posts.empty? %>
2
+ <% each_post do |post, new_date| %>
3
+ <div class="post">
4
+ <% if new_date %>
5
+ <p class="date"><%= new_date.strftime("%m/%d/%y") %></p>
6
+ <% end %>
7
+ <h2><a href="<%= post.url %>"><%= post.title %></a></h2>
8
+ <p class="source"><%= post.source %></p>
9
+ </div>
10
+ <% end %>
11
+ <% else %>
12
+ <p>No post fetched yet. Run the feeds:fetch rake task.</p>
13
+ <% end %>
data/views/layout.erb ADDED
@@ -0,0 +1,31 @@
1
+ <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
2
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
3
+
4
+ <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
5
+ <head>
6
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
7
+ <title><%= config['global']['title'] %> | <%= config['global']['subtitle'] %></title>
8
+ <link rel="stylesheet" href="/style.css" type="text/css" charset="utf-8" />
9
+ <link rel="alternate" href="/feed" type="application/atom+xml" title="Atom" />
10
+ </head>
11
+ <body>
12
+ <div id="container">
13
+ <div id="wrapper">
14
+ <div id="header">
15
+ <h1><a href="/" title="<%= config['global']['title'] %>"><%= config['global']['title'] %></a></h1>
16
+ <p class="subtitle"><%= config['global']['subtitle'] %></p>
17
+ </div>
18
+ <div id="content">
19
+ <%= yield %>
20
+ </div>
21
+ <div class="pagination">
22
+ <% if newer_page? %><a class="newer" href="/?page=<%= newer_page %>">« Newer</a><% end %>
23
+ <% if older_page? %><a class="older" href="/?page=<%= older_page %>">Older »</a><% end %>
24
+ </div>
25
+ </div>
26
+ <div id="footer">
27
+ Powered by <a href="http://github.com/pilu/terror/" title="Terror the micro aggregator">Terror</a>
28
+ </div>
29
+ </div>
30
+ </body>
31
+ </html>
metadata ADDED
@@ -0,0 +1,108 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: pilu-terror
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.2.1
5
+ platform: ruby
6
+ authors:
7
+ - Andrea Franz
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2009-03-16 00:00:00 -07:00
13
+ default_executable: terror
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: sinatra
17
+ type: :runtime
18
+ version_requirement:
19
+ version_requirements: !ruby/object:Gem::Requirement
20
+ requirements:
21
+ - - ">="
22
+ - !ruby/object:Gem::Version
23
+ version: 0.9.1.1
24
+ version:
25
+ - !ruby/object:Gem::Dependency
26
+ name: feed-normalizer
27
+ type: :runtime
28
+ version_requirement:
29
+ version_requirements: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: 1.5.1
34
+ version:
35
+ - !ruby/object:Gem::Dependency
36
+ name: activerecord
37
+ type: :runtime
38
+ version_requirement:
39
+ version_requirements: !ruby/object:Gem::Requirement
40
+ requirements:
41
+ - - ">="
42
+ - !ruby/object:Gem::Version
43
+ version: 2.2.2
44
+ version:
45
+ description:
46
+ email: andrea@gravityblast.com
47
+ executables:
48
+ - terror
49
+ extensions: []
50
+
51
+ extra_rdoc_files:
52
+ - README.md
53
+ - LICENSE
54
+ files:
55
+ - LICENSE
56
+ - README.md
57
+ - Rakefile
58
+ - VERSION.yml
59
+ - bin/terror
60
+ - config/Rakefile.default
61
+ - config/config.ru.default
62
+ - config/terror.yml.default
63
+ - config/thin.yml.default
64
+ - lib/tasks/feeds.rake
65
+ - lib/terror.rb
66
+ - lib/terror/feed_fetcher.rb
67
+ - lib/terror/helper.rb
68
+ - lib/terror/installer.rb
69
+ - lib/terror/post.rb
70
+ - public/style.css
71
+ - terror.gemspec
72
+ - terror_aggregator.rb
73
+ - test/feeds/example.xml
74
+ - test/post_test.rb
75
+ - test/spec_helper.rb
76
+ - test/terror_test.rb
77
+ - views/feed.builder
78
+ - views/index.erb
79
+ - views/layout.erb
80
+ has_rdoc: true
81
+ homepage: http://gravityblast.com/projects/terror/
82
+ post_install_message: Run terror projectname and start aggregating.
83
+ rdoc_options:
84
+ - --inline-source
85
+ - --charset=UTF-8
86
+ require_paths:
87
+ - lib
88
+ required_ruby_version: !ruby/object:Gem::Requirement
89
+ requirements:
90
+ - - ">="
91
+ - !ruby/object:Gem::Version
92
+ version: "0"
93
+ version:
94
+ required_rubygems_version: !ruby/object:Gem::Requirement
95
+ requirements:
96
+ - - ">="
97
+ - !ruby/object:Gem::Version
98
+ version: "0"
99
+ version:
100
+ requirements: []
101
+
102
+ rubyforge_project:
103
+ rubygems_version: 1.2.0
104
+ signing_key:
105
+ specification_version: 2
106
+ summary: Terror the micro feed aggregator
107
+ test_files: []
108
+