pilu-terror 0.2.1
Sign up to get free protection for your applications and to get access to all the features.
- data/LICENSE +20 -0
- data/README.md +31 -0
- data/Rakefile +50 -0
- data/VERSION.yml +4 -0
- data/bin/terror +11 -0
- data/config/Rakefile.default +5 -0
- data/config/config.ru.default +16 -0
- data/config/terror.yml.default +17 -0
- data/config/thin.yml.default +11 -0
- data/lib/tasks/feeds.rake +9 -0
- data/lib/terror/feed_fetcher.rb +29 -0
- data/lib/terror/helper.rb +35 -0
- data/lib/terror/installer.rb +47 -0
- data/lib/terror/post.rb +27 -0
- data/lib/terror.rb +49 -0
- data/public/style.css +141 -0
- data/terror.gemspec +41 -0
- data/terror_aggregator.rb +23 -0
- data/test/feeds/example.xml +28 -0
- data/test/post_test.rb +39 -0
- data/test/spec_helper.rb +6 -0
- data/test/terror_test.rb +17 -0
- data/views/feed.builder +27 -0
- data/views/index.erb +13 -0
- data/views/layout.erb +31 -0
- metadata +108 -0
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
data/bin/terror
ADDED
@@ -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,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
|
data/lib/terror/post.rb
ADDED
@@ -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
|
data/test/spec_helper.rb
ADDED
data/test/terror_test.rb
ADDED
@@ -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
|
data/views/feed.builder
ADDED
@@ -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
|
+
|