tobacco 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +18 -0
- data/.rspec +2 -0
- data/.rvmrc +1 -0
- data/.travis.yml +2 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +319 -0
- data/Rakefile +9 -0
- data/lib/tobacco/burnout.rb +28 -0
- data/lib/tobacco/error.rb +38 -0
- data/lib/tobacco/exhaler.rb +26 -0
- data/lib/tobacco/inhaler.rb +13 -0
- data/lib/tobacco/roller.rb +38 -0
- data/lib/tobacco/smoker.rb +141 -0
- data/lib/tobacco/version.rb +3 -0
- data/lib/tobacco.rb +46 -0
- data/spec/spec_helper.rb +18 -0
- data/spec/support/vcr_cassettes/url_content.yml +72 -0
- data/spec/tobacco/burnout_spec.rb +51 -0
- data/spec/tobacco/error_spec.rb +86 -0
- data/spec/tobacco/exhaler_spec.rb +63 -0
- data/spec/tobacco/inhaler_spec.rb +38 -0
- data/spec/tobacco/roller_spec.rb +108 -0
- data/spec/tobacco/smoker_spec.rb +204 -0
- data/spec/tobacco_spec.rb +61 -0
- data/tobacco.gemspec +24 -0
- metadata +160 -0
data/lib/tobacco.rb
ADDED
@@ -0,0 +1,46 @@
|
|
1
|
+
# A convenience module to gain access to
|
2
|
+
# configuration options
|
3
|
+
#
|
4
|
+
require 'open-uri'
|
5
|
+
|
6
|
+
module Tobacco
|
7
|
+
|
8
|
+
# Default options in the event no configuration
|
9
|
+
# file is created
|
10
|
+
#
|
11
|
+
@base_path = '/tmp/published_content'
|
12
|
+
@published_host = 'http://localhost:3000'
|
13
|
+
@content_method = :content
|
14
|
+
@content_url_method = :content_url
|
15
|
+
@output_filepath_method = :output_filepath
|
16
|
+
|
17
|
+
class << self
|
18
|
+
attr_accessor :base_path,
|
19
|
+
:published_host,
|
20
|
+
:content_method,
|
21
|
+
:content_url_method,
|
22
|
+
:output_filepath_method
|
23
|
+
end
|
24
|
+
|
25
|
+
def self.configure
|
26
|
+
yield self
|
27
|
+
end
|
28
|
+
|
29
|
+
def self.log(msg)
|
30
|
+
msg = "LOGGING: #{msg}"
|
31
|
+
|
32
|
+
if defined? Rails
|
33
|
+
Rails.logger.info(msg)
|
34
|
+
else
|
35
|
+
puts msg
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
require 'tobacco/burnout.rb'
|
41
|
+
require 'tobacco/smoker'
|
42
|
+
require 'tobacco/roller'
|
43
|
+
require 'tobacco/inhaler'
|
44
|
+
require 'tobacco/exhaler'
|
45
|
+
require 'tobacco/error'
|
46
|
+
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
require 'tobacco'
|
2
|
+
require 'pry'
|
3
|
+
require 'vcr'
|
4
|
+
|
5
|
+
RSpec.configure do |config|
|
6
|
+
config.treat_symbols_as_metadata_keys_with_true_values = true
|
7
|
+
config.run_all_when_everything_filtered = true
|
8
|
+
config.filter_run :focus
|
9
|
+
config.order = 'random'
|
10
|
+
|
11
|
+
VCR.configure do |c|
|
12
|
+
c.cassette_library_dir = 'spec/support/vcr_cassettes'
|
13
|
+
c.hook_into :fakeweb
|
14
|
+
c.allow_http_connections_when_no_cassette = true
|
15
|
+
end
|
16
|
+
|
17
|
+
config.extend VCR::RSpec::Macros
|
18
|
+
end
|
@@ -0,0 +1,72 @@
|
|
1
|
+
---
|
2
|
+
http_interactions:
|
3
|
+
- request:
|
4
|
+
method: get
|
5
|
+
uri: http://www.iana.org/domains/example/
|
6
|
+
body:
|
7
|
+
encoding: US-ASCII
|
8
|
+
string: ''
|
9
|
+
headers:
|
10
|
+
accept:
|
11
|
+
- ! '*/*'
|
12
|
+
user-agent:
|
13
|
+
- Ruby
|
14
|
+
response:
|
15
|
+
status:
|
16
|
+
code: 200
|
17
|
+
message: OK
|
18
|
+
headers:
|
19
|
+
date:
|
20
|
+
- Sun, 30 Sep 2012 02:45:14 GMT
|
21
|
+
server:
|
22
|
+
- Apache/2.2.3 (CentOS)
|
23
|
+
last-modified:
|
24
|
+
- Wed, 09 Feb 2011 17:13:15 GMT
|
25
|
+
vary:
|
26
|
+
- Accept-Encoding
|
27
|
+
connection:
|
28
|
+
- close
|
29
|
+
transfer-encoding:
|
30
|
+
- chunked
|
31
|
+
content-type:
|
32
|
+
- text/html; charset=UTF-8
|
33
|
+
body:
|
34
|
+
encoding: US-ASCII
|
35
|
+
string: ! "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\"
|
36
|
+
\"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">\n<html xmlns=\"http://www.w3.org/1999/xhtml\">\n<head>\n\t<title>IANA
|
37
|
+
— Example domains</title>\n\t<!-- start common-head -->\n\t<meta http-equiv=\"Content-type\"
|
38
|
+
content=\"text/html; charset=utf-8\" />\n\t<link rel=\"stylesheet\" type=\"text/css\"
|
39
|
+
href=\"/_css/2008.1/reset-fonts-grids.css\" />\n\t<link rel=\"stylesheet\"
|
40
|
+
type=\"text/css\" media=\"screen\" href=\"/_css/2008.1/screen.css\" />\n\t<link
|
41
|
+
rel=\"stylesheet\" type=\"text/css\" media=\"print\" href=\"/_css/2008.1/print.css\"
|
42
|
+
/>\n\t<link rel=\"shortcut icon\" type=\"image/ico\" href=\"/favicon.ico\"
|
43
|
+
/>\n\t<script type=\"text/javascript\" src=\"/_js/prototype.js\"></script>\n\t<script
|
44
|
+
type=\"text/javascript\" src=\"/_js/corners.js\"></script>\n\t<script type=\"text/javascript\"
|
45
|
+
src=\"/_js/common.js\"></script>\n\t<!-- end common-head -->\n\n</head>\n<body>\n\t<!--
|
46
|
+
start common-bodyhead -->\n\t<div id=\"header-frame\">\n\t<div id=\"header\">\n\t<div
|
47
|
+
id=\"header-logo\"><a href=\"/\"><img src=\"/_img/iana-logo-pageheader.png\"
|
48
|
+
alt=\"Homepage\"/></a></div>\n\t<div id=\"header-nav\">\n\t<ul>\n\t<li><a
|
49
|
+
href=\"/domains/\">Domains</a></li>\n\t<li><a href=\"/numbers/\">Numbers</a></li>\n\t<li><a
|
50
|
+
href=\"/protocols/\">Protocols</a></li>\n\t<li><a href=\"/about/\">About IANA</a></li>\n\t</ul>\n\t</div>\n\t</div>\n\t</div>\n\n\t<div
|
51
|
+
id=\"body-container\">\n\t<div id=\"body\">\n\t<!-- end common-bodyhead -->\n\n\n\t<h1>Example
|
52
|
+
Domains</h1>\n\n\t<p>As described in <a href=\"/go/rfc2606\">RFC 2606</a>,\n\twe
|
53
|
+
maintain a number of domains such as EXAMPLE.COM and EXAMPLE.ORG\n\tfor documentation
|
54
|
+
purposes. These domains may be used as illustrative\n\texamples in documents
|
55
|
+
without prior coordination with us. They are \n\tnot available for registration.</p>\n\n\t
|
56
|
+
<!-- start common-bodytail -->\n\t</div>\n\t</div>\n\n\t<div id=\"footer-frame\">\n\t<div
|
57
|
+
id=\"footer\">\n\n\n\t<table width=100%>\n\t<tr>\n\t\t<td id=\"iana-footer-first\"><b><a
|
58
|
+
href=\"/about/\">About</a></b><br/>\n <a href=\"/about/presentations/\">Presentations</a><br/>\n
|
59
|
+
\ <a href=\"/about/performance/\">Performance</a><br/>\n\t\t<a
|
60
|
+
href=\"/reports/\">Reports</a><br/>\n </td>\n\n\t\t<td><b><a
|
61
|
+
href=\"/domains/\">Domains</a></b><br/>\n\t\t<a href=\"/domains/root/\">Root
|
62
|
+
Zone</a><br/>\n\t\t<a href=\"/domains/int/\">.INT</a><br/>\n\t\t<a href=\"/domains/arpa/\">.ARPA</a><br/>\n\t\t<a
|
63
|
+
href=\"/domains/idn-tables/\">IDN Repository</a></td>\n\n\t\t<td><b><a href=\"/protocols/\">Protocols</a></b><br/>\n\t\t<br/>\n\t\t<b><a
|
64
|
+
href=\"/numbers/\">Number Resources</a></b><br/>\n\t\t<a href=\"/abuse/\">Abuse
|
65
|
+
Information</a></td>\n\n\t\t<td id=\"iana-footer-icann\"><img src=\"/_img/icann-logo-micro.png\"><br/>IANA
|
66
|
+
is operated by the<br/><a href=\"http://www.icann.org/\">Internet Corporation
|
67
|
+
for Assigned Names and Numbers</a></td>\n\t</tr>\n\t</table>\n\n<div id=\"footer-beta-feedback\">\n
|
68
|
+
\ <p>Please direct general feedback regarding IANA to <a href=\"mailto:iana@iana.org?subject=General%20website%20feedback\">iana@iana.org</a>.</p>\n
|
69
|
+
\ </div>\n\n\t</div>\n\t</div>\n\t<!-- end common-bodytail -->\n\n\n</body>\n</html>\n"
|
70
|
+
http_version: '1.1'
|
71
|
+
recorded_at: Sun, 30 Sep 2012 02:45:14 GMT
|
72
|
+
recorded_with: VCR 2.2.5
|
@@ -0,0 +1,51 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Tobacco::Burnout do
|
4
|
+
subject { Tobacco::Burnout }
|
5
|
+
|
6
|
+
let(:call_count) { 0 }
|
7
|
+
let(:max_timeouts) { 0 }
|
8
|
+
|
9
|
+
let(:untimely_block) {
|
10
|
+
->(call_count, max_timeouts) {
|
11
|
+
if call_count < max_timeouts
|
12
|
+
call_count += 1
|
13
|
+
|
14
|
+
raise Timeout::Error
|
15
|
+
else
|
16
|
+
return "an important value"
|
17
|
+
end
|
18
|
+
}
|
19
|
+
}
|
20
|
+
|
21
|
+
context "when the attempted block times out exactly X times" do
|
22
|
+
before do
|
23
|
+
max_timeouts = 5
|
24
|
+
end
|
25
|
+
|
26
|
+
it "raises nothing" do
|
27
|
+
expect {
|
28
|
+
subject.try(5) { untimely_block.call(call_count, max_timeouts) }
|
29
|
+
}.to_not raise_error
|
30
|
+
end
|
31
|
+
|
32
|
+
it "returns whatever the block returned" do
|
33
|
+
subject.try(5) { untimely_block.call(call_count, max_timeouts) }.should == "an important value"
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
context "when the attempted block times out more than X times" do
|
38
|
+
before do
|
39
|
+
max_timeouts = 3
|
40
|
+
end
|
41
|
+
|
42
|
+
it "raises a MaximumAttemptsExceeded exception" do
|
43
|
+
expect {
|
44
|
+
subject.try(2) do
|
45
|
+
untimely_block.call(0, 4)
|
46
|
+
end
|
47
|
+
}.to raise_error(Tobacco::Burnout::MaximumAttemptsExceeded, /more than 2 time/)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
@@ -0,0 +1,86 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'tobacco'
|
3
|
+
|
4
|
+
shared_examples_for "attributes set" do
|
5
|
+
it 'msg is set' do
|
6
|
+
error.msg.should == msg
|
7
|
+
end
|
8
|
+
|
9
|
+
it 'filepath is set' do
|
10
|
+
error.filepath.should == filepath
|
11
|
+
end
|
12
|
+
|
13
|
+
it 'content is set' do
|
14
|
+
error.content.should == content
|
15
|
+
end
|
16
|
+
|
17
|
+
it 'object is an error object' do
|
18
|
+
error.object.message.should == error_object.message
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
describe Tobacco::Error do
|
23
|
+
subject { Tobacco::Error }
|
24
|
+
|
25
|
+
describe '#new' do
|
26
|
+
context 'sets attributes during initialize' do
|
27
|
+
let(:msg) { 'Error Writing' }
|
28
|
+
let(:filepath) { '/users/somepath/index.html' }
|
29
|
+
let(:content) { '<h1>Page Title</h1>'}
|
30
|
+
let(:error_object) { RuntimeError.new("Can't do that!") }
|
31
|
+
|
32
|
+
let(:error) do
|
33
|
+
subject.new(
|
34
|
+
msg: msg,
|
35
|
+
filepath: filepath,
|
36
|
+
content: content,
|
37
|
+
object: error_object
|
38
|
+
)
|
39
|
+
end
|
40
|
+
|
41
|
+
it_behaves_like "attributes set"
|
42
|
+
end
|
43
|
+
|
44
|
+
context 'allows setting attributes directly' do
|
45
|
+
let(:msg) { 'Error Writing' }
|
46
|
+
let(:filepath) { '/users/no_permissions_here/index.html' }
|
47
|
+
let(:content) { '<h1>Overdone</h1>'}
|
48
|
+
let(:error_object) { Exception.new("I know this is crazy. Not sure what just happened. Try it again and the error may not happen... maybe") }
|
49
|
+
let(:error) { subject.new }
|
50
|
+
|
51
|
+
before do
|
52
|
+
error.msg = msg
|
53
|
+
error.filepath = filepath
|
54
|
+
error.content = content
|
55
|
+
error.object = error_object
|
56
|
+
end
|
57
|
+
|
58
|
+
it_behaves_like "attributes set"
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
describe '#to_a' do
|
63
|
+
context 'when destructuring using *splat' do
|
64
|
+
let(:msg) { 'Error Writing' }
|
65
|
+
let(:filepath) { '/users/somepath/index.html' }
|
66
|
+
let(:content) { '<h1>Page Title</h1>'}
|
67
|
+
let(:error_object) { RuntimeError.new("Can't do that!") }
|
68
|
+
|
69
|
+
let(:expected_array) { [ msg, filepath, content, error_object ] }
|
70
|
+
|
71
|
+
let(:error) do
|
72
|
+
subject.new(
|
73
|
+
msg: msg,
|
74
|
+
filepath: filepath,
|
75
|
+
content: content,
|
76
|
+
object: error_object
|
77
|
+
)
|
78
|
+
end
|
79
|
+
|
80
|
+
it 'sets the array of variables' do
|
81
|
+
splatted = *error
|
82
|
+
expected_array.should == splatted
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'tobacco'
|
3
|
+
|
4
|
+
describe Tobacco::Exhaler do
|
5
|
+
|
6
|
+
subject { Tobacco::Exhaler }
|
7
|
+
|
8
|
+
let(:exhaler) { subject.new(content, filepath) }
|
9
|
+
|
10
|
+
describe '#new' do
|
11
|
+
context 'when passing arguments' do
|
12
|
+
let(:content) { 'File content' }
|
13
|
+
let(:filepath) { '/users/someuser/work/index.html' }
|
14
|
+
|
15
|
+
it 'sets the content' do
|
16
|
+
exhaler.filepath.should == filepath
|
17
|
+
end
|
18
|
+
|
19
|
+
it 'sets the filepath' do
|
20
|
+
exhaler.content.should == content
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
context 'when setting attributes after creation' do
|
25
|
+
let(:exhaler) { Tobacco::Exhaler.new }
|
26
|
+
|
27
|
+
it 'sets the content' do
|
28
|
+
exhaler.content = 'new content'
|
29
|
+
|
30
|
+
exhaler.content.should == 'new content'
|
31
|
+
end
|
32
|
+
|
33
|
+
it 'sets the filepath' do
|
34
|
+
exhaler.filepath = '/var/other'
|
35
|
+
|
36
|
+
exhaler.filepath.should == '/var/other'
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
describe 'file operations' do
|
42
|
+
let(:filepath) { '/tmp/base_dir/index.html' }
|
43
|
+
let(:dir) { '/tmp/base_dir/' }
|
44
|
+
let(:content) { '<h1>Page Title</h1>' }
|
45
|
+
|
46
|
+
after { FileUtils.rm_rf(dir) }
|
47
|
+
|
48
|
+
describe '#create_directory' do
|
49
|
+
it 'creates a directory based on a filepath' do
|
50
|
+
exhaler.create_directory
|
51
|
+
|
52
|
+
File.exists?(dir).should be_true
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
describe '#write!' do
|
57
|
+
it 'writes content to a file' do
|
58
|
+
exhaler.write!
|
59
|
+
File.read(filepath).should == content
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'tobacco'
|
3
|
+
|
4
|
+
describe Tobacco::Inhaler do
|
5
|
+
|
6
|
+
subject { Tobacco::Inhaler.new(url) }
|
7
|
+
|
8
|
+
describe '#new' do
|
9
|
+
let!(:url) { 'http://google.com/videos' }
|
10
|
+
|
11
|
+
it 'sets the url' do
|
12
|
+
subject.url.should == url
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
describe '#read' do
|
17
|
+
|
18
|
+
context 'when reading a url succeeds' do
|
19
|
+
let!(:url) { 'http://www.iana.org/domains/example/' }
|
20
|
+
|
21
|
+
before { VCR.insert_cassette('url_content') }
|
22
|
+
after { VCR.eject_cassette() }
|
23
|
+
|
24
|
+
it 'reads content from a url' do
|
25
|
+
subject.read
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
context 'when reading a url fails' do
|
30
|
+
let!(:url) { 'http://somehost' }
|
31
|
+
|
32
|
+
it 'errors on reading' do
|
33
|
+
expect { subject.read }.to raise_error(Tobacco::Burnout::MaximumAttemptsExceeded)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|
@@ -0,0 +1,108 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'tobacco'
|
3
|
+
|
4
|
+
describe Tobacco::Roller do
|
5
|
+
|
6
|
+
context 'paths' do
|
7
|
+
before do
|
8
|
+
Tobacco.configure do |config|
|
9
|
+
config.published_host = 'http://localhost:3000'
|
10
|
+
config.base_path = 'published_content'
|
11
|
+
config.content_method = :content
|
12
|
+
config.content_url_method = :content_url
|
13
|
+
config.output_filepath_method = :output_filepath
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
subject { Tobacco::Roller.new(smoker) }
|
18
|
+
|
19
|
+
context 'when using default settings' do
|
20
|
+
let(:smoker) { double('smoker') }
|
21
|
+
let(:expected_output_filepath) { 'published_content/1/video/245' }
|
22
|
+
|
23
|
+
describe '#output_filepath' do
|
24
|
+
it 'builds a path when given array of options' do
|
25
|
+
smoker.stub(:output_filepath) { [ '1', 'video', '245' ] }
|
26
|
+
|
27
|
+
subject.output_filepath.should == expected_output_filepath
|
28
|
+
end
|
29
|
+
|
30
|
+
it 'builds a path when given a string' do
|
31
|
+
smoker.stub(:output_filepath) { '1/video/245' }
|
32
|
+
|
33
|
+
subject.output_filepath.should == expected_output_filepath
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
describe '#content_url' do
|
38
|
+
context 'when prepended with a forward slash' do
|
39
|
+
let(:expected_content_url) { 'http://localhost:3000/publisher/video/245' }
|
40
|
+
|
41
|
+
it 'builds a content url' do
|
42
|
+
smoker.stub(:content_url) { '/publisher/video/245' }
|
43
|
+
|
44
|
+
subject.content_url.should == expected_content_url
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
context 'when not prepended with a forward slash' do
|
49
|
+
let(:expected_content_url) { 'http://localhost:3000/publisher/video/245' }
|
50
|
+
|
51
|
+
it 'builds a content url' do
|
52
|
+
smoker.stub(:content_url) { 'publisher/video/245' }
|
53
|
+
|
54
|
+
subject.content_url.should == expected_content_url
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
|
61
|
+
context 'when overriding default settings' do
|
62
|
+
let(:smoker) { double('smoker') }
|
63
|
+
|
64
|
+
before do
|
65
|
+
Tobacco.configure do |config|
|
66
|
+
config.published_host = 'http://localhost:5000'
|
67
|
+
config.base_path = 'pub_content'
|
68
|
+
config.content_method = :data
|
69
|
+
config.content_url_method = :url
|
70
|
+
config.output_filepath_method = :fpath
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
let(:smoker) { double('smoker') }
|
75
|
+
let(:expected_output_filepath) { 'pub_content/1/video/245' }
|
76
|
+
|
77
|
+
describe '#output_filepath' do
|
78
|
+
it 'builds an ouput filepath' do
|
79
|
+
smoker.stub(:fpath) { [ '1', 'video', '245' ] }
|
80
|
+
|
81
|
+
subject.output_filepath.should == expected_output_filepath
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
describe '#content_url' do
|
86
|
+
context 'when prepended with a forward slash' do
|
87
|
+
let(:expected_content_url) { 'http://localhost:5000/publisher/video/245' }
|
88
|
+
|
89
|
+
it 'builds a content url' do
|
90
|
+
smoker.stub(:url) { '/publisher/video/245' }
|
91
|
+
|
92
|
+
subject.content_url.should == expected_content_url
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
context 'when not prepended with a forward slash' do
|
97
|
+
let(:expected_content_url) { 'http://localhost:5000/publisher/video/245' }
|
98
|
+
|
99
|
+
it 'builds a content url' do
|
100
|
+
smoker.stub(:url) { 'publisher/video/245' }
|
101
|
+
|
102
|
+
subject.content_url.should == expected_content_url
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
@@ -0,0 +1,204 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'tobacco'
|
3
|
+
|
4
|
+
|
5
|
+
describe Tobacco::Smoker do
|
6
|
+
before do
|
7
|
+
Tobacco.configure do |config|
|
8
|
+
config.published_host = 'http://localhost:3000'
|
9
|
+
config.base_path = '/tmp/published_content'
|
10
|
+
config.content_method = :content
|
11
|
+
config.content_url_method = :content_url
|
12
|
+
config.output_filepath_method = :output_filepath
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
let(:smoker) { mock('smoker') }
|
17
|
+
|
18
|
+
subject { Tobacco::Smoker.new(smoker) }
|
19
|
+
|
20
|
+
context '#write!' do
|
21
|
+
|
22
|
+
context 'when content is empty' do
|
23
|
+
let(:inhaler) { mock('inhaler', read: '') }
|
24
|
+
let(:filepath) { mock('roller', content_url: '/video', filepath: '/user') }
|
25
|
+
|
26
|
+
before do
|
27
|
+
Tobacco::Inhaler.should_receive(:new).and_return(inhaler)
|
28
|
+
subject.file_path_generator = filepath
|
29
|
+
end
|
30
|
+
|
31
|
+
it 'does not attempt a write' do
|
32
|
+
Tobacco::Exhaler.should_not_receive(:new)
|
33
|
+
|
34
|
+
subject.read
|
35
|
+
subject.write!
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
context 'content' do
|
40
|
+
let(:content) { 'Directly set content' }
|
41
|
+
let(:exhaler) { mock('exhaler', write!: true) }
|
42
|
+
let(:filepath) { mock('roller', content_url: '/video', filepath: '/user', output_filepath: '/desktop') }
|
43
|
+
|
44
|
+
before do
|
45
|
+
Tobacco::Exhaler.should_receive(:new).and_return(exhaler)
|
46
|
+
subject.file_path_generator = filepath
|
47
|
+
end
|
48
|
+
|
49
|
+
context 'when providing content directly' do
|
50
|
+
|
51
|
+
it 'allows setting content directly' do
|
52
|
+
exhaler.should_receive(:write!)
|
53
|
+
subject.content = content
|
54
|
+
|
55
|
+
subject.write!
|
56
|
+
end
|
57
|
+
|
58
|
+
it 'callback :on_success is called' do
|
59
|
+
smoker.should_receive(:before_write).with(content).and_return(content)
|
60
|
+
smoker.should_receive(:on_success).with(content)
|
61
|
+
subject.content = content
|
62
|
+
|
63
|
+
subject.write!
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
context 'when an error occurs during writing' do
|
68
|
+
let(:error) { raise RuntimeError.new('File Permission Error') }
|
69
|
+
|
70
|
+
before do
|
71
|
+
subject.file_path_generator = filepath
|
72
|
+
smoker.should_receive(:before_write).with(content).and_return(content)
|
73
|
+
exhaler.should_receive(:write!).and_return { error }
|
74
|
+
end
|
75
|
+
|
76
|
+
it 'calls the callback :on_write_error' do
|
77
|
+
smoker.should_receive(:on_write_error)
|
78
|
+
subject.content = content
|
79
|
+
|
80
|
+
subject.write!
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
describe '#modify_content_before_writing' do
|
87
|
+
let(:content) { '<h1>Summer Gear</h1>' }
|
88
|
+
let(:modified_content) { '<h1>Winter Gear</h1>' }
|
89
|
+
|
90
|
+
before do
|
91
|
+
smoker.stub(:before_write).and_return(modified_content)
|
92
|
+
end
|
93
|
+
|
94
|
+
it 'allows the smoker to modify content before writing' do
|
95
|
+
subject.content = content
|
96
|
+
|
97
|
+
subject.modify_content_before_writing.should == modified_content
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
describe '#generate_file_paths' do
|
102
|
+
it 'sets the file_path_generator' do
|
103
|
+
Tobacco::Roller.should_receive(:new).with(smoker)
|
104
|
+
|
105
|
+
subject.generate_file_paths
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
describe '#choose_reader' do
|
110
|
+
context 'when smoker provides the content' do
|
111
|
+
before { smoker.stub(:content) }
|
112
|
+
|
113
|
+
it 'uses the smoker for the content' do
|
114
|
+
subject.choose_reader
|
115
|
+
subject.reader.should == smoker
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
context 'when smoker does not provide content' do
|
120
|
+
let(:inhaler) { mock('inhaler', read: 'reader method') }
|
121
|
+
let(:filepath) { mock('roller', content_url: '/video') }
|
122
|
+
|
123
|
+
before do
|
124
|
+
Tobacco::Inhaler.should_receive(:new).and_return(inhaler)
|
125
|
+
subject.file_path_generator = filepath
|
126
|
+
end
|
127
|
+
|
128
|
+
it 'uses the smoker for the content' do
|
129
|
+
subject.choose_reader
|
130
|
+
subject.reader.should == inhaler
|
131
|
+
end
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
describe '#read_content' do
|
136
|
+
context 'when content is read from the smoker' do
|
137
|
+
let(:content) { 'Provided content' }
|
138
|
+
|
139
|
+
before { smoker.stub(:content).and_return(content) }
|
140
|
+
|
141
|
+
it 'should have the correct content' do
|
142
|
+
subject.choose_reader
|
143
|
+
subject.read_content
|
144
|
+
|
145
|
+
subject.content.should == content
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
149
|
+
context 'when content is read from url' do
|
150
|
+
let(:content) { 'Content from reading url' }
|
151
|
+
let(:inhaler) { mock('inhaler', read: content) }
|
152
|
+
let(:filepath) { mock('roller', content_url: '/video') }
|
153
|
+
|
154
|
+
before do
|
155
|
+
Tobacco::Inhaler.should_receive(:new).and_return(inhaler)
|
156
|
+
subject.file_path_generator = filepath
|
157
|
+
end
|
158
|
+
|
159
|
+
it 'should have the correct content' do
|
160
|
+
subject.choose_reader
|
161
|
+
subject.read_content
|
162
|
+
|
163
|
+
subject.content.should == content
|
164
|
+
end
|
165
|
+
end
|
166
|
+
end
|
167
|
+
|
168
|
+
describe '#read' do
|
169
|
+
context 'when content is empty' do
|
170
|
+
class Writer
|
171
|
+
attr_accessor :error
|
172
|
+
def on_read_error(error)
|
173
|
+
self.error = error
|
174
|
+
end
|
175
|
+
end
|
176
|
+
|
177
|
+
describe '#on_read_error' do
|
178
|
+
let(:inhaler) { mock('inhaler', read: '') }
|
179
|
+
let(:filepath) { mock('roller', content_url: '/video', filepath: '/file/path') }
|
180
|
+
let(:smoker) { Writer.new }
|
181
|
+
|
182
|
+
before do
|
183
|
+
Tobacco::Inhaler.should_receive(:new).and_return(inhaler)
|
184
|
+
subject.file_path_generator = filepath
|
185
|
+
end
|
186
|
+
|
187
|
+
it 'the callback is called on the smoker' do
|
188
|
+
subject.smoker.should_receive(:on_read_error)
|
189
|
+
|
190
|
+
subject.read
|
191
|
+
end
|
192
|
+
|
193
|
+
it 'has the correct message on the error object' do
|
194
|
+
subject.read
|
195
|
+
|
196
|
+
error = subject.smoker.error
|
197
|
+
message = error.object.message
|
198
|
+
|
199
|
+
message.should match(/No error encountered/)
|
200
|
+
end
|
201
|
+
end
|
202
|
+
end
|
203
|
+
end
|
204
|
+
end
|