audrey2 0.2.1 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README.rdoc +14 -0
- data/Rakefile +4 -2
- data/VERSION +1 -1
- data/audrey2.conf.sample +1 -1
- data/audrey2.gemspec +31 -5
- data/bin/feedme +8 -39
- data/lib/audrey2.rb +121 -38
- data/test/helper.rb +35 -0
- data/test/test_config.rb +148 -0
- data/test/test_email.rb +202 -0
- data/test/test_misc.rb +50 -0
- data/test/test_options.rb +69 -0
- data/test/test_parse.rb +87 -0
- data/test/test_recipes.rb +99 -0
- data/test/test_sort.rb +65 -0
- data/test/test_themes.rb +110 -0
- metadata +58 -9
data/README.rdoc
CHANGED
@@ -7,6 +7,20 @@ Audrey 2.0 is a gem for feed processing and aggregation.
|
|
7
7
|
The initial codebase is an adaptation of several other prototypes. It suffers from
|
8
8
|
severe shortages of documentation and testing. Both are coming soon.
|
9
9
|
|
10
|
+
== Release notes
|
11
|
+
|
12
|
+
The 0.3.0 version of Audrey 2.0 makes three changes which could break compatibility with
|
13
|
+
existing installations. They are all very easy to deal with:
|
14
|
+
|
15
|
+
1) The default location of the configuration file has moved from /etc/audrey2/audrey2.conf
|
16
|
+
to /etc/audrey2.conf
|
17
|
+
|
18
|
+
2) In a theme's entry.haml when you reference data in the entry hash you now need to index
|
19
|
+
the hash using symbols rather than strings: entry[:title] replaces entry['title'].
|
20
|
+
|
21
|
+
3) If email notification of errors is configured via SMTP you now need to identify the
|
22
|
+
SMTP server with the key 'address' instead of 'server' in Audrey 2.0's configuration file.
|
23
|
+
|
10
24
|
== Installation
|
11
25
|
|
12
26
|
gem install audrey2
|
data/Rakefile
CHANGED
@@ -5,13 +5,15 @@ begin
|
|
5
5
|
require 'jeweler'
|
6
6
|
Jeweler::Tasks.new do |gem|
|
7
7
|
gem.name = "audrey2"
|
8
|
-
gem.summary = "
|
9
|
-
gem.description = "
|
8
|
+
gem.summary = "Feed processing and aggregation"
|
9
|
+
gem.description = "Audrey 2.0 is a command-line utility for customizable feed processing, aggregation, and output."
|
10
10
|
gem.email = "sven.aas@gmail.com"
|
11
11
|
gem.homepage = "http://github.com/svenaas/audrey2"
|
12
12
|
gem.authors = ["Sven Aas"]
|
13
13
|
gem.add_dependency "feed-normalizer", "~>1.5.2"
|
14
14
|
gem.add_dependency "haml", "~>3.0.13"
|
15
|
+
gem.add_development_dependency "shoulda", ">= 2.11.1"
|
16
|
+
gem.add_development_dependency "mocha", ">= 0.9.8"
|
15
17
|
# gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
|
16
18
|
end
|
17
19
|
Jeweler::GemcutterTasks.new
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.
|
1
|
+
0.3.0
|
data/audrey2.conf.sample
CHANGED
data/audrey2.gemspec
CHANGED
@@ -5,13 +5,13 @@
|
|
5
5
|
|
6
6
|
Gem::Specification.new do |s|
|
7
7
|
s.name = %q{audrey2}
|
8
|
-
s.version = "0.
|
8
|
+
s.version = "0.3.0"
|
9
9
|
|
10
10
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
11
|
s.authors = ["Sven Aas"]
|
12
|
-
s.date = %q{2010-
|
12
|
+
s.date = %q{2010-08-17}
|
13
13
|
s.default_executable = %q{feedme}
|
14
|
-
s.description = %q{
|
14
|
+
s.description = %q{Audrey 2.0 is a command-line utility for customizable feed processing, aggregation, and output.}
|
15
15
|
s.email = %q{sven.aas@gmail.com}
|
16
16
|
s.executables = ["feedme"]
|
17
17
|
s.extra_rdoc_files = [
|
@@ -28,13 +28,33 @@ Gem::Specification.new do |s|
|
|
28
28
|
"audrey2.conf.sample",
|
29
29
|
"audrey2.gemspec",
|
30
30
|
"bin/feedme",
|
31
|
-
"lib/audrey2.rb"
|
31
|
+
"lib/audrey2.rb",
|
32
|
+
"test/helper.rb",
|
33
|
+
"test/test_config.rb",
|
34
|
+
"test/test_email.rb",
|
35
|
+
"test/test_misc.rb",
|
36
|
+
"test/test_options.rb",
|
37
|
+
"test/test_parse.rb",
|
38
|
+
"test/test_recipes.rb",
|
39
|
+
"test/test_sort.rb",
|
40
|
+
"test/test_themes.rb"
|
32
41
|
]
|
33
42
|
s.homepage = %q{http://github.com/svenaas/audrey2}
|
34
43
|
s.rdoc_options = ["--charset=UTF-8"]
|
35
44
|
s.require_paths = ["lib"]
|
36
45
|
s.rubygems_version = %q{1.3.7}
|
37
|
-
s.summary = %q{
|
46
|
+
s.summary = %q{Feed processing and aggregation}
|
47
|
+
s.test_files = [
|
48
|
+
"test/helper.rb",
|
49
|
+
"test/test_config.rb",
|
50
|
+
"test/test_email.rb",
|
51
|
+
"test/test_misc.rb",
|
52
|
+
"test/test_options.rb",
|
53
|
+
"test/test_parse.rb",
|
54
|
+
"test/test_recipes.rb",
|
55
|
+
"test/test_sort.rb",
|
56
|
+
"test/test_themes.rb"
|
57
|
+
]
|
38
58
|
|
39
59
|
if s.respond_to? :specification_version then
|
40
60
|
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
|
@@ -43,13 +63,19 @@ Gem::Specification.new do |s|
|
|
43
63
|
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
|
44
64
|
s.add_runtime_dependency(%q<feed-normalizer>, ["~> 1.5.2"])
|
45
65
|
s.add_runtime_dependency(%q<haml>, ["~> 3.0.13"])
|
66
|
+
s.add_development_dependency(%q<shoulda>, [">= 2.11.1"])
|
67
|
+
s.add_development_dependency(%q<mocha>, [">= 0.9.8"])
|
46
68
|
else
|
47
69
|
s.add_dependency(%q<feed-normalizer>, ["~> 1.5.2"])
|
48
70
|
s.add_dependency(%q<haml>, ["~> 3.0.13"])
|
71
|
+
s.add_dependency(%q<shoulda>, [">= 2.11.1"])
|
72
|
+
s.add_dependency(%q<mocha>, [">= 0.9.8"])
|
49
73
|
end
|
50
74
|
else
|
51
75
|
s.add_dependency(%q<feed-normalizer>, ["~> 1.5.2"])
|
52
76
|
s.add_dependency(%q<haml>, ["~> 3.0.13"])
|
77
|
+
s.add_dependency(%q<shoulda>, [">= 2.11.1"])
|
78
|
+
s.add_dependency(%q<mocha>, [">= 0.9.8"])
|
53
79
|
end
|
54
80
|
end
|
55
81
|
|
data/bin/feedme
CHANGED
@@ -1,45 +1,14 @@
|
|
1
1
|
#!/usr/bin/ruby
|
2
|
-
|
3
2
|
require 'rubygems'
|
4
3
|
require 'audrey2'
|
5
|
-
require 'optparse'
|
6
|
-
|
7
|
-
options = {}
|
8
|
-
|
9
|
-
opts = OptionParser.new do |opts|
|
10
|
-
opts.banner = "Usage: feedme [OPTIONS] recipes"
|
11
|
-
|
12
|
-
options[:config] = '/etc/audrey2/audrey2.conf'
|
13
|
-
opts.on( '--config CONFIGFILE', "Location of config file", "(default: /etc/audrey2/audrey2.conf)" ) do |f|
|
14
|
-
options[:config] = f
|
15
|
-
end
|
16
|
-
|
17
|
-
opts.on_tail( '-h', '--help', 'Display this screen' ) do
|
18
|
-
puts opts
|
19
|
-
exit
|
20
|
-
end
|
21
|
-
end
|
22
|
-
|
23
|
-
begin
|
24
|
-
opts.parse! ARGV
|
25
|
-
options
|
26
|
-
rescue OptionParser::InvalidOption => e
|
27
|
-
$stderr.puts e
|
28
|
-
$stderr.puts opts
|
29
|
-
exit 1
|
30
|
-
rescue OptionParser::MissingArgument => e
|
31
|
-
$stderr.puts e
|
32
|
-
$stderr.puts opts
|
33
|
-
exit 1
|
34
|
-
end
|
35
4
|
|
5
|
+
options = Audrey2::Options.parse(ARGV)
|
6
|
+
|
36
7
|
recipes = ARGV
|
37
|
-
if recipes.length == 0
|
38
|
-
$stderr.puts "You must specify at least one recipe to feed me"
|
39
|
-
$stderr.puts opts
|
40
|
-
exit 1
|
41
|
-
end
|
42
8
|
|
43
|
-
|
44
|
-
|
45
|
-
|
9
|
+
if recipes.empty?
|
10
|
+
abort "You must specify at least one recipe to feed me"
|
11
|
+
else
|
12
|
+
audrey2 = Audrey2::Aggregator.new(options[:config])
|
13
|
+
recipes.each { |recipe| audrey2.feed_me(recipe) }
|
14
|
+
end
|
data/lib/audrey2.rb
CHANGED
@@ -3,8 +3,59 @@ require 'yaml'
|
|
3
3
|
require 'feed-normalizer'
|
4
4
|
require 'open-uri'
|
5
5
|
require 'haml'
|
6
|
+
require 'optparse'
|
7
|
+
|
8
|
+
module HashExtensions # Adapted from http://gist.github.com/151324 by Avdi Grimm and Paul Berry
|
9
|
+
def recursively_symbolize_keys
|
10
|
+
inject({}) do |acc, (k,v)|
|
11
|
+
key = String === k ? k.to_sym : k
|
12
|
+
value = case v
|
13
|
+
when Hash
|
14
|
+
v.recursively_symbolize_keys
|
15
|
+
when Array
|
16
|
+
v.collect { |e| Hash === e ? e.recursively_symbolize_keys : e }
|
17
|
+
else
|
18
|
+
v
|
19
|
+
end
|
20
|
+
acc[key] = value
|
21
|
+
acc
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
Hash.send(:include, HashExtensions)
|
6
26
|
|
7
27
|
module Audrey2
|
28
|
+
class Options
|
29
|
+
def self.parse(args)
|
30
|
+
options = {}
|
31
|
+
|
32
|
+
opts = OptionParser.new do |opts|
|
33
|
+
opts.banner = "Usage: feedme [OPTIONS] recipes"
|
34
|
+
|
35
|
+
options[:config] = '/etc/audrey2.conf'
|
36
|
+
opts.on( '--config CONFIGFILE', "Location of config file", "(default: /etc/audrey2.conf)" ) do |f|
|
37
|
+
options[:config] = f
|
38
|
+
end
|
39
|
+
|
40
|
+
opts.on_tail( '-h', '--help', 'Display this screen' ) do
|
41
|
+
puts opts
|
42
|
+
exit
|
43
|
+
end
|
44
|
+
|
45
|
+
begin
|
46
|
+
opts.parse! args
|
47
|
+
options
|
48
|
+
rescue OptionParser::ParseError => e
|
49
|
+
warn e.message
|
50
|
+
$stderr.puts opts
|
51
|
+
exit 1
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
options
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
8
59
|
|
9
60
|
class Aggregator
|
10
61
|
def initialize(configfile)
|
@@ -15,19 +66,21 @@ module Audrey2
|
|
15
66
|
begin
|
16
67
|
# Load recipe and theme and make sure everything is in order
|
17
68
|
recipe = load_recipe(recipe_name)
|
18
|
-
|
19
|
-
output_file = recipe['output_file']
|
69
|
+
output_file = recipe[:output_file]
|
20
70
|
verify_output_file(output_file)
|
21
|
-
max_entries = recipe[
|
71
|
+
max_entries = recipe[:max_entries] || 1000
|
72
|
+
|
73
|
+
# Load theme
|
74
|
+
init_theme(recipe[:theme])
|
22
75
|
|
23
76
|
# Download and parse the feeds
|
24
77
|
entry_sources = {}
|
25
|
-
feeds = recipe[
|
78
|
+
feeds = recipe[:feeds].collect { |feed| parse_feed(feed, entry_sources) }
|
26
79
|
|
27
80
|
# Aggregate and sort the entries
|
28
81
|
entries = []
|
29
82
|
feeds.each { |feed| entries += feed.entries }
|
30
|
-
sort!
|
83
|
+
entries.sort! &entry_sort_comparator
|
31
84
|
|
32
85
|
# Prepare template evaluation scope including any helper code defined in the theme
|
33
86
|
scope = Object.new
|
@@ -42,8 +95,9 @@ module Audrey2
|
|
42
95
|
end
|
43
96
|
|
44
97
|
File.open(output_file, 'w') { |f| f << output }
|
45
|
-
|
46
98
|
rescue Exception => e
|
99
|
+
# NOTE: This also catches SystemExit as can be raise by Kernel#exit when recipes
|
100
|
+
# and themes are loaded and verified. Is it good to handle those events here? Perhaps ...
|
47
101
|
if @email
|
48
102
|
email(<<-EOF
|
49
103
|
An exception occurred while running recipe #{recipe_name}
|
@@ -67,24 +121,25 @@ EOF
|
|
67
121
|
return unless @email
|
68
122
|
|
69
123
|
mail = Mail.new
|
70
|
-
mail[:from] = @email[
|
71
|
-
mail[:to] = @email[
|
124
|
+
mail[:from] = @email[:from]
|
125
|
+
mail[:to] = @email[:to]
|
72
126
|
mail[:subject] = "[AUDREY 2.0] Exception Notification"
|
73
127
|
mail[:body] = e
|
74
|
-
|
75
|
-
|
76
|
-
|
128
|
+
|
129
|
+
@email[:via] ||= 'sendmail' # Do this somewhere else?
|
130
|
+
case @email[:via].to_sym # Do this somewhere else too?
|
131
|
+
when :sendmail
|
77
132
|
mail.delivery_method :sendmail
|
78
|
-
when
|
79
|
-
raise "Missing SMTP configuration" unless @email[
|
133
|
+
when :smtp
|
134
|
+
raise "Missing SMTP configuration" unless @email[:smtp]
|
80
135
|
smtp = {
|
81
|
-
:address => @email[
|
82
|
-
:port => @email[
|
136
|
+
:address => @email[:smtp][:address] || 'localhost',
|
137
|
+
:port => @email[:smtp][:port] || 25
|
83
138
|
}
|
84
|
-
smtp[:domain] = @email[
|
85
|
-
smtp[:user_name] = @email[
|
86
|
-
smtp[:password] = @email[
|
87
|
-
smtp[:authentication] = @email[
|
139
|
+
smtp[:domain] = @email[:smtp][:domain] if @email[:smtp][:domain]
|
140
|
+
smtp[:user_name] = @email[:smtp][:user_name] if @email[:smtp][:user_name]
|
141
|
+
smtp[:password] = @email[:smtp][:password] if @email[:smtp][:password]
|
142
|
+
smtp[:authentication] = @email[:smtp][:authentication] if @email[:smtp][:authentication]
|
88
143
|
mail.delivery_method :smtp, smtp
|
89
144
|
end
|
90
145
|
|
@@ -129,38 +184,47 @@ EOF
|
|
129
184
|
@helper_code = nil
|
130
185
|
if File.exist? helper_file
|
131
186
|
if ! File.readable? helper_file
|
132
|
-
$stderr.puts "ERROR:
|
187
|
+
$stderr.puts "ERROR: Helpers file #{helper_file} is not readable"
|
133
188
|
exit(1)
|
134
189
|
end
|
135
190
|
@helper_code = File.open(helper_file) { |f| f.read }
|
136
191
|
end
|
137
192
|
end
|
138
193
|
|
139
|
-
#
|
140
|
-
def sort
|
141
|
-
case
|
142
|
-
when
|
143
|
-
|
194
|
+
# Implements sort orders which may be specified in configuration
|
195
|
+
def entry_sort_comparator(sort = @sort) # Defaults to the sort order specified in configuration
|
196
|
+
case sort
|
197
|
+
when :reverse_chronological
|
198
|
+
Proc.new {|a, b| b.date_published <=> a.date_published }
|
199
|
+
when :chronological
|
200
|
+
Proc.new {|a, b| a.date_published <=> b.date_published }
|
144
201
|
end
|
145
202
|
end
|
146
203
|
|
147
204
|
def parse_feed(feed, entry_sources)
|
148
205
|
remote_feed = nil
|
149
206
|
begin
|
150
|
-
remote_feed = open(feed[
|
207
|
+
remote_feed = open(feed[:url], "User-Agent" => @user_agent)
|
151
208
|
rescue Exception => e
|
152
|
-
raise "Exception occurred when opening feed #{feed[
|
209
|
+
raise "Exception occurred when opening feed #{feed[:name]} at #{feed[:url]}:\n\n" + e.to_s
|
153
210
|
end
|
154
211
|
|
155
212
|
parsed_feed = nil
|
156
|
-
begin
|
213
|
+
begin :test
|
157
214
|
parsed_feed = FeedNormalizer::FeedNormalizer.parse remote_feed
|
158
215
|
rescue Exception => e
|
159
|
-
raise "Exception occurred when parsing feed #{feed[
|
216
|
+
raise "Exception occurred when parsing feed #{feed[:name]} which was downloaded from #{feed[:url]}:\n\n" + e.to_s
|
160
217
|
end
|
161
218
|
|
162
|
-
raise "Feed #{feed[
|
219
|
+
raise "Feed #{feed[:name]} at #{feed[:url]} does not appear to be a parsable feed" unless parsed_feed
|
163
220
|
|
221
|
+
# Sort and truncate the entries if max_entries argument is present
|
222
|
+
if feed[:max_entries]
|
223
|
+
parsed_feed.entries.sort!(&entry_sort_comparator)
|
224
|
+
parsed_feed.entries.slice!(feed[:max_entries], parsed_feed.entries.size - feed[:max_entries])
|
225
|
+
end
|
226
|
+
|
227
|
+
# Store the entry sources. TODO: Store this information in the entries themselves
|
164
228
|
parsed_feed.entries.each { |entry| entry_sources[entry] = feed }
|
165
229
|
|
166
230
|
parsed_feed
|
@@ -169,13 +233,24 @@ EOF
|
|
169
233
|
def load_recipe(recipe)
|
170
234
|
recipefile = File.join(@recipes_folder, recipe)
|
171
235
|
if ! File.exist? recipefile
|
172
|
-
$stderr.puts "ERROR: Recipe #{recipefile} does not exist"
|
236
|
+
$stderr.puts "ERROR: Recipe file #{recipefile} does not exist"
|
173
237
|
exit(1)
|
174
238
|
elsif ! File.readable? recipefile
|
175
239
|
$stderr.puts "ERROR: Recipe file #{recipefile} is not readable"
|
176
240
|
exit(1)
|
177
241
|
end
|
178
|
-
|
242
|
+
|
243
|
+
recipe = {}
|
244
|
+
|
245
|
+
begin
|
246
|
+
recipe = YAML::load_file(recipefile).recursively_symbolize_keys
|
247
|
+
rescue Exception => e
|
248
|
+
$stderr.puts "ERROR: Problem parsing recipe file #{recipefile}"
|
249
|
+
$stderr.puts e
|
250
|
+
exit(1)
|
251
|
+
end
|
252
|
+
|
253
|
+
recipe
|
179
254
|
end
|
180
255
|
|
181
256
|
def init_config(configfile)
|
@@ -187,9 +262,17 @@ EOF
|
|
187
262
|
exit(1)
|
188
263
|
end
|
189
264
|
|
190
|
-
config =
|
265
|
+
config = {}
|
266
|
+
|
267
|
+
begin
|
268
|
+
config = YAML::load_file(configfile).recursively_symbolize_keys
|
269
|
+
rescue Exception => e
|
270
|
+
$stderr.puts "ERROR: Problem parsing configuration file #{configfile}"
|
271
|
+
$stderr.puts e
|
272
|
+
exit(1)
|
273
|
+
end
|
191
274
|
|
192
|
-
@recipes_folder = config[
|
275
|
+
@recipes_folder = config[:recipes_folder]
|
193
276
|
if ! File.exist? @recipes_folder
|
194
277
|
$stderr.puts "ERROR: Recipes folder #{@recipes_folder} does not exist"
|
195
278
|
exit(1)
|
@@ -198,7 +281,7 @@ EOF
|
|
198
281
|
exit(1)
|
199
282
|
end
|
200
283
|
|
201
|
-
@themes_folder = config[
|
284
|
+
@themes_folder = config[:themes_folder]
|
202
285
|
if ! File.exist? @themes_folder
|
203
286
|
$stderr.puts "ERROR: Themes folder #{@themes_folder} does not exist"
|
204
287
|
exit(1)
|
@@ -207,10 +290,10 @@ EOF
|
|
207
290
|
exit(1)
|
208
291
|
end
|
209
292
|
|
210
|
-
@user_agent = config[
|
211
|
-
@sort = config[
|
293
|
+
@user_agent = config[:user_agent] || 'Audrey 2.0 Feed Aggregator'
|
294
|
+
@sort = (config[:sort] || 'reverse_chronological').to_sym
|
212
295
|
|
213
|
-
if @email = config[
|
296
|
+
if @email = config[:email]
|
214
297
|
gem 'mail', '~> 2.2.5'
|
215
298
|
require 'mail'
|
216
299
|
# TODO: Check for required/consistent email config
|
data/test/helper.rb
ADDED
@@ -0,0 +1,35 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
gem 'test-unit'
|
3
|
+
require 'test/unit'
|
4
|
+
require 'shoulda'
|
5
|
+
require 'mocha'
|
6
|
+
|
7
|
+
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
8
|
+
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'bin'))
|
9
|
+
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
10
|
+
require 'audrey2'
|
11
|
+
|
12
|
+
# The following trick is via http://thinkingdigitally.com/archive/capturing-output-from-puts-in-ruby/
|
13
|
+
require 'stringio'
|
14
|
+
module Kernel
|
15
|
+
def capture_stderr
|
16
|
+
err = StringIO.new
|
17
|
+
$stderr = err
|
18
|
+
yield
|
19
|
+
return err
|
20
|
+
ensure
|
21
|
+
$stderr = STDERR
|
22
|
+
end
|
23
|
+
|
24
|
+
def capture_stdout
|
25
|
+
out = StringIO.new
|
26
|
+
$stdout = out
|
27
|
+
yield
|
28
|
+
return out
|
29
|
+
ensure
|
30
|
+
$stdout = STDOUT
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
class Test::Unit::TestCase
|
35
|
+
end
|
data/test/test_config.rb
ADDED
@@ -0,0 +1,148 @@
|
|
1
|
+
require 'helper'
|
2
|
+
|
3
|
+
class TestConfig < Test::Unit::TestCase
|
4
|
+
context "Initializing an Aggregator" do
|
5
|
+
|
6
|
+
context "without a configfile" do
|
7
|
+
should 'report error and exit' do
|
8
|
+
File.stubs(:exist?).with('configfile').returns(false)
|
9
|
+
err = capture_stderr { assert_raise(SystemExit) { Audrey2::Aggregator.new('configfile') } }
|
10
|
+
assert_match /ERROR: Configuration file configfile does not exist/, err.string
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
context "with an unreadable configfile" do
|
15
|
+
should 'report error and exit' do
|
16
|
+
File.stubs(:exist?).with('configfile').returns(true)
|
17
|
+
File.expects(:readable?).with('configfile').returns(false)
|
18
|
+
err = capture_stderr { assert_raise(SystemExit) { Audrey2::Aggregator.new('configfile') } }
|
19
|
+
assert_match /ERROR: Configuration file configfile is not readable/, err.string
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
context "with an unparsable configfile" do
|
24
|
+
should 'report error and exit' do
|
25
|
+
File.stubs(:exist?).with('configfile').returns(true)
|
26
|
+
File.stubs(:readable?).with('configfile').returns(true)
|
27
|
+
YAML.expects(:load_file).with('configfile').raises(Exception)
|
28
|
+
err = capture_stderr { assert_raise(SystemExit) { Audrey2::Aggregator.new('configfile') } }
|
29
|
+
assert_match /ERROR: Problem parsing configuration file configfile/, err.string
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
context "with a valid configfile" do
|
34
|
+
setup do
|
35
|
+
@config
|
36
|
+
File.stubs(:exist?).with('configfile').returns(true)
|
37
|
+
File.stubs(:readable?).with('configfile').returns(true)
|
38
|
+
YAML.stubs(:load_file).with('configfile').returns({
|
39
|
+
'recipes_folder' => 'recipes_folder',
|
40
|
+
'themes_folder' => 'themes_folder'
|
41
|
+
})
|
42
|
+
end
|
43
|
+
|
44
|
+
context "without a recipes folder" do
|
45
|
+
should 'report error and exit' do
|
46
|
+
File.expects(:exist?).with('recipes_folder').returns(false)
|
47
|
+
err = capture_stderr { assert_raise(SystemExit) { Audrey2::Aggregator.new('configfile') } }
|
48
|
+
assert err.string =~ /ERROR: Recipes folder recipes_folder does not exist/
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
context "with an unreadable recipes folder" do
|
53
|
+
should 'report error and exit' do
|
54
|
+
File.stubs(:exist?).with('recipes_folder').returns(true)
|
55
|
+
File.expects(:readable?).with('recipes_folder').returns(false)
|
56
|
+
err = capture_stderr { assert_raise(SystemExit) { Audrey2::Aggregator.new('configfile') } }
|
57
|
+
assert err.string =~ /ERROR: Recipes folder recipes_folder is not readable/
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
context "and recipes folder" do
|
62
|
+
setup do
|
63
|
+
File.stubs(:exist?).with('recipes_folder').returns(true)
|
64
|
+
File.stubs(:readable?).with('recipes_folder').returns(true)
|
65
|
+
end
|
66
|
+
|
67
|
+
context "without a themes folder" do
|
68
|
+
should 'report error and exit' do
|
69
|
+
File.expects(:exist?).with('themes_folder').returns(false)
|
70
|
+
err = capture_stderr { assert_raise(SystemExit) { Audrey2::Aggregator.new('configfile') } }
|
71
|
+
assert err.string =~ /ERROR: Themes folder themes_folder does not exist/
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
context "with an unreadable themes folder" do
|
76
|
+
should 'report error and exit' do
|
77
|
+
File.stubs(:exist?).with('themes_folder').returns(true)
|
78
|
+
File.expects(:readable?).with('themes_folder').returns(false)
|
79
|
+
err = capture_stderr { assert_raise(SystemExit) { Audrey2::Aggregator.new('configfile') } }
|
80
|
+
assert err.string =~ /ERROR: Themes folder themes_folder is not readable/
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
context "and themes folder" do
|
85
|
+
setup do
|
86
|
+
File.stubs(:exist?).with('themes_folder').returns(true)
|
87
|
+
File.stubs(:readable?).with('themes_folder').returns(true)
|
88
|
+
@aggregator = Audrey2::Aggregator.new('configfile')
|
89
|
+
end
|
90
|
+
|
91
|
+
should "return a valid aggregator" do
|
92
|
+
assert_not_nil @aggregator
|
93
|
+
end
|
94
|
+
|
95
|
+
should "use the default user agent" do
|
96
|
+
assert_equal 'Audrey 2.0 Feed Aggregator', @aggregator.instance_variable_get('@user_agent')
|
97
|
+
end
|
98
|
+
|
99
|
+
should "use the default sort" do
|
100
|
+
assert_equal :reverse_chronological, @aggregator.instance_variable_get('@sort')
|
101
|
+
end
|
102
|
+
|
103
|
+
should "not setup email" do
|
104
|
+
assert_nil @aggregator.instance_variable_get('@email')
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
context "with a complete config" do
|
111
|
+
setup do
|
112
|
+
@config
|
113
|
+
File.stubs(:exist?).with('configfile').returns(true)
|
114
|
+
File.stubs(:readable?).with('configfile').returns(true)
|
115
|
+
YAML.stubs(:load_file).with('configfile').returns({
|
116
|
+
'recipes_folder' => 'recipes_folder',
|
117
|
+
'themes_folder' => 'themes_folder',
|
118
|
+
'user_agent' => 'user_agent',
|
119
|
+
'sort' => 'sort',
|
120
|
+
'email' => 'email'
|
121
|
+
})
|
122
|
+
File.stubs(:exist?).with('recipes_folder').returns(true)
|
123
|
+
File.stubs(:readable?).with('recipes_folder').returns(true)
|
124
|
+
File.stubs(:exist?).with('themes_folder').returns(true)
|
125
|
+
File.stubs(:readable?).with('themes_folder').returns(true)
|
126
|
+
@aggregator = Audrey2::Aggregator.new('configfile')
|
127
|
+
end
|
128
|
+
|
129
|
+
should "return a valid aggregator" do
|
130
|
+
assert_not_nil @aggregator
|
131
|
+
end
|
132
|
+
|
133
|
+
should "use the specified user agent" do
|
134
|
+
assert_equal 'user_agent', @aggregator.instance_variable_get('@user_agent')
|
135
|
+
end
|
136
|
+
|
137
|
+
should "use the specified sort" do
|
138
|
+
assert_equal :sort, @aggregator.instance_variable_get('@sort')
|
139
|
+
end
|
140
|
+
|
141
|
+
# TODO: Fix this one later once email config consistency checking (and testing) are implemented
|
142
|
+
should "setup email" do
|
143
|
+
assert_equal 'email', @aggregator.instance_variable_get('@email')
|
144
|
+
end
|
145
|
+
|
146
|
+
end
|
147
|
+
end
|
148
|
+
end
|