audrey2 0.2.1 → 0.3.0
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/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
|