tobacco 0.0.1
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/.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
|