audio-feed-manager 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +27 -0
- data/.rspec +2 -0
- data/Gemfile +4 -0
- data/Guardfile +17 -0
- data/LICENSE.txt +22 -0
- data/README.md +86 -0
- data/Rakefile +2 -0
- data/audio-feed-manager.gemspec +32 -0
- data/bin/afm +5 -0
- data/lib/audio-feed-manager.rb +1 -0
- data/lib/audio_feed_manager/application.rb +39 -0
- data/lib/audio_feed_manager/audio_file.rb +9 -0
- data/lib/audio_feed_manager/audio_file_adder.rb +14 -0
- data/lib/audio_feed_manager/audio_file_repository.rb +42 -0
- data/lib/audio_feed_manager/cli/add_audio_file.rb +39 -0
- data/lib/audio_feed_manager/cli/add_feed.rb +20 -0
- data/lib/audio_feed_manager/cli/arguments.rb +27 -0
- data/lib/audio_feed_manager/cli/arguments_parser.rb +67 -0
- data/lib/audio_feed_manager/cli/arguments_specification.rb +103 -0
- data/lib/audio_feed_manager/cli/command.rb +23 -0
- data/lib/audio_feed_manager/cli/commands_list.rb +120 -0
- data/lib/audio_feed_manager/cli/get_feed_url.rb +20 -0
- data/lib/audio_feed_manager/cli/initialize_project.rb +44 -0
- data/lib/audio_feed_manager/cli/list_feeds.rb +38 -0
- data/lib/audio_feed_manager/cli/publish.rb +21 -0
- data/lib/audio_feed_manager/cli/show_feed.rb +44 -0
- data/lib/audio_feed_manager/cli/update_feed_rss.rb +19 -0
- data/lib/audio_feed_manager/cli.rb +19 -0
- data/lib/audio_feed_manager/config.rb +5 -0
- data/lib/audio_feed_manager/config_repository.rb +14 -0
- data/lib/audio_feed_manager/console.rb +28 -0
- data/lib/audio_feed_manager/directory_lister.rb +9 -0
- data/lib/audio_feed_manager/error.rb +4 -0
- data/lib/audio_feed_manager/feed.rb +9 -0
- data/lib/audio_feed_manager/feed_items_repository.rb +30 -0
- data/lib/audio_feed_manager/feed_repository.rb +32 -0
- data/lib/audio_feed_manager/feed_syncer.rb +20 -0
- data/lib/audio_feed_manager/file_storage.rb +20 -0
- data/lib/audio_feed_manager/id3_tags.rb +14 -0
- data/lib/audio_feed_manager/new_model_creator.rb +15 -0
- data/lib/audio_feed_manager/rss_generator.rb +10 -0
- data/lib/audio_feed_manager/rss_repository.rb +36 -0
- data/lib/audio_feed_manager/s3_gateway.rb +39 -0
- data/lib/audio_feed_manager/secret_token_generator.rb +9 -0
- data/lib/audio_feed_manager/stop_application.rb +4 -0
- data/lib/audio_feed_manager/support/getter_setter_method.rb +18 -0
- data/lib/audio_feed_manager/support/hash_constructor.rb +9 -0
- data/lib/audio_feed_manager/tags.rb +9 -0
- data/lib/audio_feed_manager/unique_id_generator.rb +19 -0
- data/lib/audio_feed_manager/url_maker.rb +13 -0
- data/lib/audio_feed_manager/version.rb +3 -0
- data/lib/audio_feed_manager.rb +57 -0
- data/spec/audio_feed_manager/audio_file_adder_spec.rb +49 -0
- data/spec/audio_feed_manager/audio_file_repository_spec.rb +58 -0
- data/spec/audio_feed_manager/cli/add_audio_file_spec.rb +44 -0
- data/spec/audio_feed_manager/cli/add_feed_spec.rb +27 -0
- data/spec/audio_feed_manager/cli/arguments_parser_spec.rb +85 -0
- data/spec/audio_feed_manager/cli/arguments_specification_spec.rb +51 -0
- data/spec/audio_feed_manager/cli/commands_list_spec.rb +91 -0
- data/spec/audio_feed_manager/cli/get_feed_url_spec.rb +26 -0
- data/spec/audio_feed_manager/cli/initialize_project_spec.rb +47 -0
- data/spec/audio_feed_manager/cli/list_feeds_spec.rb +27 -0
- data/spec/audio_feed_manager/cli/show_feed_spec.rb +41 -0
- data/spec/audio_feed_manager/cli/update_feed_rss_spec.rb +26 -0
- data/spec/audio_feed_manager/cli_spec.rb +42 -0
- data/spec/audio_feed_manager/directory_lister_spec.rb +23 -0
- data/spec/audio_feed_manager/feed_items_repository_spec.rb +34 -0
- data/spec/audio_feed_manager/feeds_repository_spec.rb +40 -0
- data/spec/audio_feed_manager/file_storage_spec.rb +22 -0
- data/spec/audio_feed_manager/id3_tags_spec.rb +26 -0
- data/spec/audio_feed_manager/new_model_creator_spec.rb +44 -0
- data/spec/audio_feed_manager/rss_repository_spec.rb +34 -0
- data/spec/audio_feed_manager/s3_gateway_spec.rb +51 -0
- data/spec/audio_feed_manager/secret_token_generator_spec.rb +13 -0
- data/spec/audio_feed_manager/support/getter_setter_method_spec.rb +77 -0
- data/spec/audio_feed_manager/unique_id_generator_spec.rb +23 -0
- data/spec/fixtures/test.mp3 +0 -0
- data/spec/fixtures/test.txt +1 -0
- data/spec/help_spec.rb +64 -0
- data/spec/initializing_spec.rb +12 -0
- data/spec/managing_audio_files_spec.rb +21 -0
- data/spec/managing_feeds_spec.rb +51 -0
- data/spec/publishing_spec.rb +24 -0
- data/spec/s3_credentials.yml.sample +5 -0
- data/spec/spec_helper.rb +17 -0
- data/spec/support/builders.rb +26 -0
- data/spec/support/fake_console.rb +13 -0
- data/spec/support/fakes.rb +5 -0
- data/spec/support/integration_helpers.rb +38 -0
- metadata +297 -0
@@ -0,0 +1,26 @@
|
|
1
|
+
module AudioFeedManager
|
2
|
+
describe UpdateFeedRss do
|
3
|
+
fake(:feed_repository)
|
4
|
+
fake(:rss_generator)
|
5
|
+
let(:console) { FakeConsole.new }
|
6
|
+
|
7
|
+
let(:update_feed_rss) { isolate(UpdateFeedRss) }
|
8
|
+
|
9
|
+
it "generates the feed RSS" do
|
10
|
+
feed = build_feed
|
11
|
+
stub(feed_repository).fetch("feeds/foo") { feed }
|
12
|
+
|
13
|
+
update_feed_rss.run(feed: "feeds/foo")
|
14
|
+
|
15
|
+
expect(rss_generator).to have_received.generate(feed)
|
16
|
+
end
|
17
|
+
|
18
|
+
it "prints an error message when feed not found" do
|
19
|
+
stub(feed_repository).fetch("feeds/foo") { raise FeedNotFound.for_id("feeds/foo") }
|
20
|
+
|
21
|
+
update_feed_rss.run(feed: "feeds/foo")
|
22
|
+
|
23
|
+
expect(console.out).to include("not found")
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
module AudioFeedManager
|
2
|
+
describe CLI do
|
3
|
+
fake(:command, arguments: ArgumentsSpecification.new, name: "init") { InitializeProject }
|
4
|
+
fake(:commands_list)
|
5
|
+
fake(:arguments_parser)
|
6
|
+
fake(:help)
|
7
|
+
fake(:console)
|
8
|
+
let(:arguments) { Arguments.from_hash(project_dir: "my-project-name") }
|
9
|
+
|
10
|
+
let(:cli) { isolate(CLI) }
|
11
|
+
|
12
|
+
it "runs the specified command" do
|
13
|
+
stub(commands_list).get(["init", "my-project-name"]) { [command, ["my-project-name"]]}
|
14
|
+
stub(arguments_parser).parse(command.arguments, ["my-project-name"]) { arguments }
|
15
|
+
|
16
|
+
cli.run(["init", "my-project-name"])
|
17
|
+
|
18
|
+
expect(command).to have_received.run(project_dir: "my-project-name")
|
19
|
+
end
|
20
|
+
|
21
|
+
it "prints command usage if command arguments are invalid and stops the application" do
|
22
|
+
stub(commands_list).get(["init"]) { [command, []]}
|
23
|
+
stub(arguments_parser).parse(command.arguments, []) { raise InvalidArguments }
|
24
|
+
|
25
|
+
expect {
|
26
|
+
cli.run(["init"])
|
27
|
+
}.to raise_error(StopApplication)
|
28
|
+
|
29
|
+
expect(help).to have_received.print_command_usage("init")
|
30
|
+
end
|
31
|
+
|
32
|
+
it "prints usage if command is invalid and stops the application" do
|
33
|
+
stub(commands_list).get(["foo"]) { raise UnknownCommand }
|
34
|
+
|
35
|
+
expect {
|
36
|
+
cli.run(["foo"])
|
37
|
+
}.to raise_error(StopApplication)
|
38
|
+
|
39
|
+
expect(help).to have_received.print_usage
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module AudioFeedManager
|
2
|
+
describe DirectoryLister do
|
3
|
+
let(:directory_lister) { isolate(DirectoryLister) }
|
4
|
+
|
5
|
+
it "returns an empty array for non-existent directories" do
|
6
|
+
files = directory_lister.list("/tmp/asdfasdfasdfasdfasdfasdf")
|
7
|
+
|
8
|
+
expect(files).to eq([])
|
9
|
+
end
|
10
|
+
|
11
|
+
it "returns a list of all the files in a given directory, sorted by name" do
|
12
|
+
FileUtils.rm_rf("/tmp/foobar")
|
13
|
+
FileUtils.mkdir_p("/tmp/foobar")
|
14
|
+
FileUtils.touch("/tmp/foobar/foo.txt")
|
15
|
+
FileUtils.touch("/tmp/foobar/bar.txt")
|
16
|
+
FileUtils.touch("/tmp/foobar/baz.txt")
|
17
|
+
|
18
|
+
files = directory_lister.list("/tmp/foobar")
|
19
|
+
|
20
|
+
expect(files).to eq(["bar.txt", "baz.txt", "foo.txt"])
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
module AudioFeedManager
|
2
|
+
describe FeedItemsRepository do
|
3
|
+
let(:items_directory) { Pathname.new("/tmp/foo") }
|
4
|
+
let(:feed_items_repository) { isolate(FeedItemsRepository) }
|
5
|
+
fake(:audio_file_repository)
|
6
|
+
|
7
|
+
before do
|
8
|
+
FileUtils.rm_rf(items_directory)
|
9
|
+
FileUtils.mkdir_p(items_directory)
|
10
|
+
end
|
11
|
+
|
12
|
+
it "returns an empty array for a feed with no items" do
|
13
|
+
feed = build_feed
|
14
|
+
|
15
|
+
expect(feed_items_repository.list(feed)).to eq([])
|
16
|
+
end
|
17
|
+
|
18
|
+
it "returns a list of audio files added for a feed, latest first" do
|
19
|
+
feed = build_feed(id: "feeds/abc")
|
20
|
+
other_feed = build_feed(id: "feeds/def")
|
21
|
+
foo = AudioFile.new(id: "audio_files/foo")
|
22
|
+
bar = AudioFile.new(id: "audio_files/bar")
|
23
|
+
baz = AudioFile.new(id: "audio_files/baz")
|
24
|
+
stub(audio_file_repository).fetch("audio_files/foo") { foo }
|
25
|
+
stub(audio_file_repository).fetch("audio_files/bar") { bar }
|
26
|
+
stub(audio_file_repository).fetch("audio_files/baz") { baz }
|
27
|
+
feed_items_repository.add(feed, foo)
|
28
|
+
feed_items_repository.add(feed, bar)
|
29
|
+
feed_items_repository.add(other_feed, baz)
|
30
|
+
|
31
|
+
expect(feed_items_repository.list(feed)).to eq([bar, foo])
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
module AudioFeedManager
|
4
|
+
describe FeedRepository do
|
5
|
+
let(:feed_repository) { isolate(FeedRepository) }
|
6
|
+
let(:feeds_directory) { Pathname.new("/tmp/foo/feeds") }
|
7
|
+
let(:feeds_prefix) { "feeds" }
|
8
|
+
fake(:new_model_creator)
|
9
|
+
fake(:storage)
|
10
|
+
fake(:directory_lister)
|
11
|
+
|
12
|
+
it "stores new feeds" do
|
13
|
+
saved_feed = Feed.new(id: "feeds/foo")
|
14
|
+
feed = Feed.new(title: "Hello World!")
|
15
|
+
mock(new_model_creator).create(feed, directory: feeds_directory,
|
16
|
+
prefix: feeds_prefix) { saved_feed }
|
17
|
+
|
18
|
+
feed = feed_repository.add(feed)
|
19
|
+
end
|
20
|
+
|
21
|
+
it "fetches the stored feeds" do
|
22
|
+
stub(storage).read("feeds/hello-world") { {id: "feeds/hello-world", title: "Hello World"} }
|
23
|
+
|
24
|
+
expect(feed_repository.fetch("feeds/hello-world")).to eq(Feed.new(id: "feeds/hello-world", title: "Hello World"))
|
25
|
+
end
|
26
|
+
|
27
|
+
it "raises FeedNotFound when a feed by given id does not exist" do
|
28
|
+
stub(storage).read("feeds/hello-world") { raise FileNotFound }
|
29
|
+
|
30
|
+
expect{feed_repository.fetch("feeds/hello-world")}.to raise_error(FeedNotFound)
|
31
|
+
end
|
32
|
+
|
33
|
+
it "lists the feeds" do
|
34
|
+
stub(directory_lister).list(feeds_directory) { ["hello-world"] }
|
35
|
+
stub(storage).read("feeds/hello-world") { {id: "feeds/hello-world", title: "Hello World"} }
|
36
|
+
|
37
|
+
expect(feed_repository.list).to include(Feed.new(id: "feeds/hello-world", title: "Hello World"))
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module AudioFeedManager
|
2
|
+
describe FileStorage do
|
3
|
+
let(:directory) { Pathname.new("/tmp/foo") }
|
4
|
+
|
5
|
+
let(:storage) { isolate(FileStorage) }
|
6
|
+
|
7
|
+
before do
|
8
|
+
FileUtils.rm_rf(directory)
|
9
|
+
FileUtils.mkdir_p(directory.join("feeds"))
|
10
|
+
end
|
11
|
+
|
12
|
+
it "stores the contents in a specified file" do
|
13
|
+
storage.store("feeds/hello", {id: 1, title: "hello!"})
|
14
|
+
|
15
|
+
expect(storage.read("feeds/hello")).to eq(id: 1, title: "hello!")
|
16
|
+
end
|
17
|
+
|
18
|
+
it "raises an error, when the given file does not exist" do
|
19
|
+
expect{ storage.read("feeds/hello") }.to raise_error(FileNotFound)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module AudioFeedManager
|
2
|
+
describe Id3Tags do
|
3
|
+
let(:id3tags) { isolate(Id3Tags) }
|
4
|
+
|
5
|
+
it "reads ID3 tags from an mp3 file" do
|
6
|
+
tags = id3tags.read(fixture_file("test.mp3"))
|
7
|
+
|
8
|
+
expect(tags.title).to eq("My first audio file")
|
9
|
+
expect(tags.artist).to eq("Adam Pohorecki")
|
10
|
+
end
|
11
|
+
|
12
|
+
it "returns empty tags for a file that does not exist" do
|
13
|
+
tags = id3tags.read(Pathname.new("/tmp/file-which-does-not-exist"))
|
14
|
+
|
15
|
+
expect(tags.title).to eq(nil)
|
16
|
+
expect(tags.artist).to eq(nil)
|
17
|
+
end
|
18
|
+
|
19
|
+
it "returns empty tags for a file that is not an mp3 file" do
|
20
|
+
tags = id3tags.read(fixture_file("test.txt"))
|
21
|
+
|
22
|
+
expect(tags.title).to eq(nil)
|
23
|
+
expect(tags.artist).to eq(nil)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
module AudioFeedManager
|
2
|
+
describe NewModelCreator do
|
3
|
+
class SampleModel < Struct.new(:id, :title, :secret_token, :foo)
|
4
|
+
include HashConstructor
|
5
|
+
end
|
6
|
+
|
7
|
+
let(:new_model_creator) { isolate(NewModelCreator) }
|
8
|
+
let(:directory) { Pathname.new("/tmp/foo/feeds") }
|
9
|
+
let(:prefix) { "feeds" }
|
10
|
+
fake(:unique_id_generator, generate: "hello-world")
|
11
|
+
fake(:secret_token_generator, generate: "secret-token")
|
12
|
+
fake(:storage)
|
13
|
+
|
14
|
+
it "sets the id for the added model" do
|
15
|
+
stub(unique_id_generator).generate("Hello World!", directory) { "hello-world" }
|
16
|
+
model = SampleModel.new(title: "Hello World!")
|
17
|
+
|
18
|
+
new_model_creator.create(model, prefix: prefix, directory: directory)
|
19
|
+
|
20
|
+
expect(model.id).to eq("feeds/hello-world")
|
21
|
+
end
|
22
|
+
|
23
|
+
it "stores the added model" do
|
24
|
+
model = SampleModel.new(title: "Hello World!", foo: "hello there")
|
25
|
+
|
26
|
+
new_model_creator.create(model, prefix: prefix, directory: directory)
|
27
|
+
|
28
|
+
expect(storage).to have_received.store(
|
29
|
+
"feeds/hello-world",
|
30
|
+
id: "feeds/hello-world",
|
31
|
+
title: "Hello World!",
|
32
|
+
secret_token: "secret-token",
|
33
|
+
foo: "hello there",
|
34
|
+
)
|
35
|
+
end
|
36
|
+
|
37
|
+
it "assigns a secret token to the created model" do
|
38
|
+
model = new_model_creator.create(SampleModel.new(title: "Hello World!"), prefix: prefix, directory: directory)
|
39
|
+
|
40
|
+
expect(model.secret_token).to eq("secret-token")
|
41
|
+
end
|
42
|
+
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
module AudioFeedManager
|
2
|
+
describe RSSRepository do
|
3
|
+
fake(:url_maker, feed_url: "http://example.com/feed.xml", audio_file_url: "http://example.com/audio/foo.mp3")
|
4
|
+
let(:rss_files_directory) { Pathname.new("/tmp/my-project/rss") }
|
5
|
+
|
6
|
+
before do
|
7
|
+
FileUtils.rm_rf(rss_files_directory)
|
8
|
+
FileUtils.mkdir_p(rss_files_directory)
|
9
|
+
end
|
10
|
+
|
11
|
+
it "stores an RSS feed under rss/:secret_token.xml" do
|
12
|
+
feed = build_feed(id: "feeds/hello",
|
13
|
+
title: "My First Feed",
|
14
|
+
description: "some description",
|
15
|
+
secret_token: "abcdefgh")
|
16
|
+
audio_file = build_audio_file(secret_token: "foobar",
|
17
|
+
extension: ".mp3",
|
18
|
+
title: "Some audio file",
|
19
|
+
author: "Audio file author")
|
20
|
+
rss_repository = isolate(RSSRepository)
|
21
|
+
|
22
|
+
rss_repository.store(feed, [audio_file])
|
23
|
+
|
24
|
+
feed_content = File.read("/tmp/my-project/rss/abcdefgh.xml")
|
25
|
+
expect(feed_content).to include("<?xml")
|
26
|
+
expect(feed_content).to include("My First Feed")
|
27
|
+
expect(feed_content).to include("some description")
|
28
|
+
expect(feed_content).not_to include("feeds/hello")
|
29
|
+
expect(feed_content).to include("Some audio file")
|
30
|
+
expect(feed_content).to include("Audio file author")
|
31
|
+
expect(feed_content).to include("http://example.com/audio/foo.mp3")
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
require 'net/http'
|
2
|
+
|
3
|
+
module AudioFeedManager
|
4
|
+
describe S3Gateway, :slow do
|
5
|
+
let(:config) { Config.new(s3_bucket: s3_config["bucket"],
|
6
|
+
s3_access_key: s3_config["access_key"],
|
7
|
+
s3_secret: s3_config["secret_key"],
|
8
|
+
s3_region: s3_config["region"]) }
|
9
|
+
|
10
|
+
let(:s3_gateway) { isolate(S3Gateway) }
|
11
|
+
|
12
|
+
it "syncs files to S3" do
|
13
|
+
unique_key = "test/#{SecureRandom.hex}"
|
14
|
+
file_name = :not_called
|
15
|
+
|
16
|
+
s3_gateway.sync(unique_key, fixture_file("test.mp3")) { |f| file_name = f }
|
17
|
+
|
18
|
+
expect(file_name).to eq(unique_key)
|
19
|
+
end
|
20
|
+
|
21
|
+
it "does not sync the same file twice" do
|
22
|
+
unique_key = "test/#{SecureRandom.hex}"
|
23
|
+
file_name = :not_called
|
24
|
+
|
25
|
+
s3_gateway.sync(unique_key, fixture_file("test.mp3"))
|
26
|
+
s3_gateway.sync(unique_key, fixture_file("test.mp3")) { |f| file_name = f }
|
27
|
+
|
28
|
+
expect(file_name).to eq(:not_called)
|
29
|
+
end
|
30
|
+
|
31
|
+
it "syncs the same file twice if the contents change" do
|
32
|
+
unique_key = "test/#{SecureRandom.hex}"
|
33
|
+
file_name = :not_called
|
34
|
+
|
35
|
+
s3_gateway.sync(unique_key, fixture_file("test.mp3"))
|
36
|
+
s3_gateway.sync(unique_key, fixture_file("test.txt")) { |f| file_name = f }
|
37
|
+
|
38
|
+
expect(file_name).to eq(unique_key)
|
39
|
+
end
|
40
|
+
|
41
|
+
it "makes the file publicly accessible" do
|
42
|
+
unique_key = "test/#{SecureRandom.hex}"
|
43
|
+
|
44
|
+
s3_gateway.sync(unique_key, fixture_file("test.txt"))
|
45
|
+
|
46
|
+
url = URI.parse(s3_gateway.url(unique_key))
|
47
|
+
content = File.read(fixture_file("test.txt"))
|
48
|
+
expect(Net::HTTP.get(url)).to eq(content)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
module AudioFeedManager
|
2
|
+
describe SecretTokenGenerator do
|
3
|
+
let(:generator) { isolate(SecretTokenGenerator) }
|
4
|
+
|
5
|
+
it "generates 16-character strings" do
|
6
|
+
expect(generator.generate.size).to eq(16)
|
7
|
+
end
|
8
|
+
|
9
|
+
it "generates unique strings" do
|
10
|
+
expect(generator.generate).not_to eq(generator.generate)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,77 @@
|
|
1
|
+
module AudioFeedManager
|
2
|
+
describe GetterSetterMethod do
|
3
|
+
context "without a default value" do
|
4
|
+
klass = Class.new do
|
5
|
+
extend GetterSetterMethod
|
6
|
+
|
7
|
+
get_set :foo
|
8
|
+
get_set :bar
|
9
|
+
end
|
10
|
+
|
11
|
+
it "returns nil by default" do
|
12
|
+
instance = klass.new
|
13
|
+
|
14
|
+
expect(instance.foo).to be_nil
|
15
|
+
expect(instance.bar).to be_nil
|
16
|
+
end
|
17
|
+
|
18
|
+
it "sets the value when value given" do
|
19
|
+
instance = klass.new
|
20
|
+
|
21
|
+
instance.foo "bar"
|
22
|
+
|
23
|
+
expect(instance.foo).to eq("bar")
|
24
|
+
end
|
25
|
+
|
26
|
+
it "does not overwrite other values when setting one property" do
|
27
|
+
instance = klass.new
|
28
|
+
|
29
|
+
instance.foo "bar"
|
30
|
+
|
31
|
+
expect(instance.bar).to be_nil
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
context "with a default value" do
|
36
|
+
klass = Class.new do
|
37
|
+
extend GetterSetterMethod
|
38
|
+
|
39
|
+
get_set(:foo) { bar }
|
40
|
+
get_set :bar
|
41
|
+
get_set(:baz) { "hello" }
|
42
|
+
end
|
43
|
+
|
44
|
+
it "returns the default value" do
|
45
|
+
instance = klass.new
|
46
|
+
|
47
|
+
expect(instance.baz).to eq("hello")
|
48
|
+
end
|
49
|
+
|
50
|
+
it "overwrites the value when value given" do
|
51
|
+
instance = klass.new
|
52
|
+
|
53
|
+
instance.baz "aaa"
|
54
|
+
|
55
|
+
expect(instance.baz).to eq("aaa")
|
56
|
+
end
|
57
|
+
|
58
|
+
it "evaluates the default block lazily" do
|
59
|
+
instance = klass.new
|
60
|
+
|
61
|
+
instance.bar "aaa"
|
62
|
+
|
63
|
+
expect(instance.foo).to eq("aaa")
|
64
|
+
end
|
65
|
+
|
66
|
+
it "caches the value once set" do
|
67
|
+
instance = klass.new
|
68
|
+
|
69
|
+
instance.bar "aaa"
|
70
|
+
instance.foo
|
71
|
+
instance.bar "bbb"
|
72
|
+
|
73
|
+
expect(instance.foo).to eq("aaa")
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module AudioFeedManager
|
2
|
+
describe UniqueIdGenerator do
|
3
|
+
fake(:directory_lister)
|
4
|
+
|
5
|
+
let(:generator) { isolate(UniqueIdGenerator) }
|
6
|
+
|
7
|
+
it "generates ids based on the given name" do
|
8
|
+
stub(directory_lister).list("/foo") { [] }
|
9
|
+
|
10
|
+
id = generator.generate("Hello World!", "/foo")
|
11
|
+
|
12
|
+
expect(id).to eq("hello-world")
|
13
|
+
end
|
14
|
+
|
15
|
+
it "suffixes the id with a number if the name is not unique" do
|
16
|
+
stub(directory_lister).list("/foo") { ["hello-world", "foo-bar"] }
|
17
|
+
|
18
|
+
id = generator.generate("Hello World!", "/foo")
|
19
|
+
|
20
|
+
expect(id).to eq("hello-world-1")
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
Binary file
|
@@ -0,0 +1 @@
|
|
1
|
+
Hello!
|
data/spec/help_spec.rb
ADDED
@@ -0,0 +1,64 @@
|
|
1
|
+
require 'stringio'
|
2
|
+
|
3
|
+
describe "help commands" do
|
4
|
+
let(:stdin) { StringIO.new }
|
5
|
+
let(:stderr) { StringIO.new }
|
6
|
+
|
7
|
+
it "prints help when no arguments given" do
|
8
|
+
AudioFeedManager.run(
|
9
|
+
argv: [],
|
10
|
+
stdin: stdin,
|
11
|
+
stderr: stderr,
|
12
|
+
)
|
13
|
+
|
14
|
+
expect(stderr.string).to include("Usage:")
|
15
|
+
expect(stderr.string).to include("init")
|
16
|
+
end
|
17
|
+
|
18
|
+
it "prints help with the help command" do
|
19
|
+
AudioFeedManager.run(
|
20
|
+
argv: ["help"],
|
21
|
+
stdin: stdin,
|
22
|
+
stderr: stderr,
|
23
|
+
)
|
24
|
+
|
25
|
+
expect(stderr.string).to include("Usage:")
|
26
|
+
expect(stderr.string).to include("init")
|
27
|
+
end
|
28
|
+
|
29
|
+
it "prints help with an unknown command" do
|
30
|
+
AudioFeedManager.run(
|
31
|
+
argv: ["asdfasdfa"],
|
32
|
+
stdin: stdin,
|
33
|
+
stderr: stderr,
|
34
|
+
exit: ->(out) {}
|
35
|
+
)
|
36
|
+
|
37
|
+
expect(stderr.string).to include("Usage:")
|
38
|
+
expect(stderr.string).to include("init")
|
39
|
+
end
|
40
|
+
|
41
|
+
it "prints help for specific commands" do
|
42
|
+
AudioFeedManager.run(
|
43
|
+
argv: ["help", "init"],
|
44
|
+
stdin: stdin,
|
45
|
+
stderr: stderr,
|
46
|
+
)
|
47
|
+
|
48
|
+
expect(stderr.string).to include("Usage:")
|
49
|
+
expect(stderr.string).to include("init")
|
50
|
+
expect(stderr.string).to include("<PROJECT_DIR>")
|
51
|
+
end
|
52
|
+
|
53
|
+
it "prints help for multi-word commands" do
|
54
|
+
AudioFeedManager.run(
|
55
|
+
argv: ["help", "feeds", "add"],
|
56
|
+
stdin: stdin,
|
57
|
+
stderr: stderr,
|
58
|
+
)
|
59
|
+
|
60
|
+
expect(stderr.string).to include("Usage:")
|
61
|
+
expect(stderr.string).to include("feeds add")
|
62
|
+
expect(stderr.string).to include("<TITLE>")
|
63
|
+
end
|
64
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
describe "Initializing project" do
|
2
|
+
it "creates the project directory and some support files/directories" do
|
3
|
+
FileUtils.rm_rf("/tmp/my-test-project")
|
4
|
+
|
5
|
+
init_project("/tmp/my-test-project")
|
6
|
+
|
7
|
+
expect(File.exists?("/tmp/my-test-project")).to be(true)
|
8
|
+
expect(File.exists?("/tmp/my-test-project/feeds")).to be(true)
|
9
|
+
expect(File.exists?("/tmp/my-test-project/audio_files")).to be(true)
|
10
|
+
expect(File.exists?("/tmp/my-test-project/config.yml")).to be(true)
|
11
|
+
end
|
12
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
describe "Managing audio files" do
|
2
|
+
it "adds audio files to feeds" do
|
3
|
+
dir = init_project
|
4
|
+
feed = add_feed(dir)
|
5
|
+
|
6
|
+
AudioFeedManager.run(
|
7
|
+
pwd: dir,
|
8
|
+
stdout: StringIO.new,
|
9
|
+
argv: ["audio", "add", feed.id, fixture_file("test.mp3")]
|
10
|
+
)
|
11
|
+
|
12
|
+
out = StringIO.new
|
13
|
+
AudioFeedManager.run(
|
14
|
+
pwd: dir,
|
15
|
+
stdout: out,
|
16
|
+
argv: ["feeds", "show", feed.id]
|
17
|
+
)
|
18
|
+
|
19
|
+
expect(out.string).to include("My first audio file")
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
describe "Managing Feeds" do
|
2
|
+
it "adds feeds using afm feeds add" do
|
3
|
+
dir = init_project
|
4
|
+
|
5
|
+
AudioFeedManager.run(
|
6
|
+
pwd: dir,
|
7
|
+
stdin: StringIO.new("some description"),
|
8
|
+
stdout: StringIO.new,
|
9
|
+
argv: ["feeds", "add", "Hello World!"]
|
10
|
+
)
|
11
|
+
|
12
|
+
stdout = StringIO.new
|
13
|
+
AudioFeedManager.run(
|
14
|
+
pwd: dir,
|
15
|
+
argv: ["feeds", "list"],
|
16
|
+
stdout: stdout,
|
17
|
+
)
|
18
|
+
|
19
|
+
expect(stdout.string).to include("Hello World!")
|
20
|
+
end
|
21
|
+
|
22
|
+
it "allows getting the feed url" do
|
23
|
+
dir = init_project
|
24
|
+
feed = add_feed(dir)
|
25
|
+
|
26
|
+
out = StringIO.new
|
27
|
+
AudioFeedManager.run(
|
28
|
+
pwd: dir,
|
29
|
+
stdout: out,
|
30
|
+
argv: ["feeds", "get-url", feed.id]
|
31
|
+
)
|
32
|
+
|
33
|
+
expect(out.string).to include("http")
|
34
|
+
end
|
35
|
+
|
36
|
+
it "allows regenerating feed rss" do
|
37
|
+
dir = init_project
|
38
|
+
feed = add_feed(dir)
|
39
|
+
|
40
|
+
rss_files = File.join(dir, "rss/*.xml")
|
41
|
+
Dir[rss_files].each{|f| FileUtils.rm_rf(f)}
|
42
|
+
|
43
|
+
AudioFeedManager.run(
|
44
|
+
pwd: dir,
|
45
|
+
stdout: StringIO.new,
|
46
|
+
argv: ["feeds", "update-rss", feed.id]
|
47
|
+
)
|
48
|
+
|
49
|
+
expect(Dir[rss_files]).not_to eq([])
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
require 'net/http'
|
2
|
+
|
3
|
+
describe "Publishing content to S3", :slow do
|
4
|
+
it "publishes the RSS and data files" do
|
5
|
+
dir = init_project
|
6
|
+
feed = add_feed(dir)
|
7
|
+
add_audio_file(dir, feed)
|
8
|
+
|
9
|
+
AudioFeedManager.run(
|
10
|
+
pwd: dir,
|
11
|
+
stdout: StringIO.new,
|
12
|
+
argv: ["publish"]
|
13
|
+
)
|
14
|
+
|
15
|
+
AudioFeedManager.run(
|
16
|
+
pwd: dir,
|
17
|
+
stdout: out = StringIO.new,
|
18
|
+
argv: ["feeds", "get-url", feed.id]
|
19
|
+
)
|
20
|
+
feed_url = out.string.chomp
|
21
|
+
|
22
|
+
expect(Net::HTTP.get(URI.parse(feed_url))).to include("Hello World!")
|
23
|
+
end
|
24
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'audio-feed-manager'
|
2
|
+
require 'bogus/rspec'
|
3
|
+
require 'dependor/rspec'
|
4
|
+
require "fileutils"
|
5
|
+
|
6
|
+
Dir[File.expand_path("../support/**/*.rb", __FILE__)].each do |file|
|
7
|
+
require file
|
8
|
+
end
|
9
|
+
|
10
|
+
Bogus.configure do |config|
|
11
|
+
config.search_modules = [AudioFeedManager]
|
12
|
+
end
|
13
|
+
|
14
|
+
RSpec.configure do |config|
|
15
|
+
config.include IntegrationHelpers
|
16
|
+
config.include Builders
|
17
|
+
end
|