tobacco 0.0.2 → 0.0.3

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.md CHANGED
@@ -297,6 +297,10 @@ end
297
297
 
298
298
  To avoid duplication, we wrap the callbacks and write! method in a helper module that is included in all the Writer classes. This makes the individual Writers very small and easy to maintain.
299
299
 
300
+ ## Future Improvements ##
301
+
302
+ Make a backup of the file before attempting a new write. If something goes wrong with the write and an empty file is created, restore the original.
303
+
300
304
 
301
305
  ## Contributing
302
306
 
@@ -0,0 +1,32 @@
1
+ module Tobacco
2
+ class Callback
3
+ attr_accessor :writer
4
+
5
+ def self.instance(writer = nil)
6
+ @instance ||= new(writer)
7
+ end
8
+ private_class_method :new
9
+
10
+ def initialize(writer = nil)
11
+ self.writer = writer
12
+ end
13
+
14
+ # Public: Notify the writer class based on callback name
15
+ # passing along data which is either content or
16
+ # an error object
17
+ #
18
+ # name - symbol - the callback name
19
+ #
20
+ # data - text or object
21
+ #
22
+ # return - data
23
+ #
24
+ def notify(name, data)
25
+ if writer.respond_to? name
26
+ writer.send(name, data)
27
+ else
28
+ data
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,48 @@
1
+ module Tobacco
2
+ class ContentReader
3
+ attr_accessor :reader, :content, :modified_content
4
+
5
+ def initialize(smoker)
6
+ @smoker = smoker
7
+ @consumer = smoker.consumer
8
+ @filepath = smoker.file_path_generator
9
+ end
10
+
11
+ def read
12
+ choose_reader
13
+ read_content
14
+ modify_content
15
+ end
16
+
17
+ def modify_content
18
+ self.modified_content = \
19
+ Tobacco::Callback.instance.notify(:before_write, content)
20
+ end
21
+
22
+ def read_content
23
+ self.content = reader.send(Tobacco.content_method)
24
+ end
25
+
26
+ # The reader will either be the calling class (consumer)
27
+ # if it provides the content method or a new Inhaler
28
+ # object that will be used to read the content from a url
29
+ #
30
+ def choose_reader
31
+ self.reader = \
32
+ if @consumer.respond_to? Tobacco.content_method
33
+ @consumer
34
+ else
35
+ Inhaler.new(@filepath.content_url).tap do |inhaler|
36
+
37
+ # Add an alias for the user configured content_method
38
+ # so that when it is called it calls :read
39
+ # on the Inhaler instance
40
+ #
41
+ inhaler.instance_eval %{
42
+ alias :"#{Tobacco.content_method}" :read
43
+ }
44
+ end
45
+ end
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,81 @@
1
+ module Tobacco
2
+ class MissingContentError < RuntimeError
3
+ def message
4
+ "No error encountered but content is empty"
5
+ end
6
+ end
7
+
8
+ class ContentValidator
9
+ attr_accessor :content
10
+
11
+ def initialize(smoker)
12
+ @smoker = smoker
13
+ self.content = smoker.content
14
+ end
15
+
16
+ # Public: Verifies that content is present and calls
17
+ # continue_write on the Smoker class if so
18
+ # and notifies the consumer class of the
19
+ # read error if not
20
+ #
21
+ def validate!
22
+ if content_present?
23
+ @smoker.continue_write
24
+ else
25
+
26
+ # ????? IS this still true
27
+ #
28
+ # At this point, the content might be an error object
29
+ # but if not, we create one
30
+ #
31
+ object = missing_content_error(content)
32
+ error = error_object('Error Reading', object)
33
+
34
+ Tobacco::Callback.instance.notify(:on_read_error, error)
35
+ end
36
+ end
37
+
38
+ #---------------------------------------------------------
39
+ private
40
+
41
+ def content_present?
42
+ @content_present ||= content?
43
+ end
44
+
45
+ def content?
46
+ return false if content.nil? || content.empty?
47
+
48
+ Array(content).last !~ /404 Not Found|The page you were looking for doesn't exist/
49
+ end
50
+
51
+ # Private: Convenience method to create a Tobacco::Error object
52
+ #
53
+ # msg - Context where the error occurred
54
+ # modified_content - Content after the :before_write callback
55
+ # e - The error that was raised
56
+ #
57
+ def error_object(msg, e)
58
+ Tobacco::Error.new(
59
+ msg: msg,
60
+ filepath: @smoker.filepath,
61
+ content: content,
62
+ object: e
63
+ )
64
+ end
65
+
66
+ # Private: Creates an error object if content is a string or nil
67
+ #
68
+ # content - A string, nil or Error object
69
+ #
70
+ # returns an Error object
71
+ #
72
+ def missing_content_error(content)
73
+ if content.respond_to? :message
74
+ content
75
+ else
76
+ Tobacco::MissingContentError.new
77
+ end
78
+ end
79
+
80
+ end
81
+ end
@@ -1,3 +1,5 @@
1
+ require 'fileutils'
2
+
1
3
  module Tobacco
2
4
  class Exhaler
3
5
  attr_accessor :content, :filepath
@@ -8,6 +10,8 @@ module Tobacco
8
10
  end
9
11
 
10
12
  def write!
13
+ safety_net.backup
14
+
11
15
  create_directory
12
16
  write_content_to_file
13
17
  end
@@ -17,10 +21,26 @@ module Tobacco
17
21
  end
18
22
 
19
23
  def write_content_to_file
24
+ begin
25
+ persist(content)
26
+ safety_net.destroy
27
+ rescue => e
28
+ safety_net.restore
29
+ raise
30
+ end
31
+ end
32
+
33
+
34
+ private
35
+
36
+ def persist(file_content)
20
37
  File.open(filepath, 'wb') do |f|
21
- f.write content
38
+ f.write file_content
22
39
  end
23
40
  end
24
41
 
42
+ def safety_net
43
+ @safety_net ||= Tobacco::SafetyNet.new(filepath)
44
+ end
25
45
  end
26
46
  end
@@ -0,0 +1,66 @@
1
+ # Backup is responsible for backing up the current file
2
+ # if it exists and restoring it in the event an error
3
+ # occurs while attempting to write new content.
4
+ #
5
+ module Tobacco
6
+ class SafetyNet
7
+ attr_reader :file
8
+
9
+ def initialize(filepath)
10
+ @filepath = filepath
11
+ end
12
+
13
+ # Public: Creates a backup of the original file
14
+ # so it can be restored if necessary
15
+ #
16
+ def backup
17
+ Tobacco.log("Backup: #{@filepath} => #{destination}")
18
+
19
+ if File.exists? @filepath
20
+ FileUtils.mv(@filepath, destination)
21
+ end
22
+ end
23
+
24
+ # Public: Restores the original file in
25
+ # the event an error occurs during
26
+ # writing the new content
27
+ #
28
+ def restore
29
+ Tobacco.log("Restoring: #{destination} => #{@filepath}")
30
+
31
+ if File.exists? destination
32
+ FileUtils.mv(destination, @filepath)
33
+ end
34
+ end
35
+
36
+ # Public: Destroys the backup file
37
+ #
38
+ def destroy
39
+ Tobacco.log("Destroying: #{destination}")
40
+
41
+ if File.exists? destination
42
+ FileUtils.rm(destination)
43
+ end
44
+ end
45
+
46
+ # Public: Memoizes the destination path
47
+ #
48
+ def destination
49
+ @destination ||= destination_path
50
+ end
51
+
52
+ private
53
+
54
+ # Private: Creates a temporary path with
55
+ # a unique name using a timestamp
56
+ #
57
+ def destination_path
58
+ name = File.basename @filepath
59
+ dir = File.dirname @filepath
60
+ additive = Time.now.to_i
61
+ temp_name = "#{additive}_#{name}"
62
+
63
+ "#{dir}/#{temp_name}"
64
+ end
65
+ end
66
+ end
@@ -1,145 +1,76 @@
1
1
  module Tobacco
2
- class MissingContentError < RuntimeError
3
- def message
4
- "No error encountered but content is empty"
5
- end
6
- end
7
-
8
2
  class Smoker
9
3
 
10
- attr_accessor :smoker,
4
+ attr_accessor :consumer,
11
5
  :file_path_generator,
12
6
  :reader,
13
- :writer,
7
+ :persister,
14
8
  :content
15
9
 
16
- def initialize(smoker, content = '')
17
- self.smoker = smoker
18
- self.content = content
10
+ def initialize(consumer, content = nil)
11
+ self.consumer = consumer
12
+ self.content = content
13
+
14
+ Tobacco::Callback.instance(consumer)
15
+
16
+ generate_file_paths
19
17
  end
20
18
 
21
19
  def generate_file_paths
22
- self.file_path_generator = Roller.new(smoker)
20
+ self.file_path_generator = Tobacco::Roller.new(consumer)
23
21
  end
24
22
 
25
23
  def read
26
- choose_reader
27
- read_content
28
- verify_content
24
+ self.content = Tobacco::ContentReader.new(self).read
29
25
  end
30
26
 
27
+ # Public: Writes content to file allowing for manipulation
28
+ # of the content beforehand through the :before_write callback
29
+ # This is due to the fact that content can be set directly
30
+ # without going through the read method.
31
+ #
32
+ # Validate is only in the write method because
33
+ # the content can be set directly and the read method
34
+ # never called.
35
+ #
31
36
  def write!
32
- return unless content_present?
37
+ Tobacco::ContentValidator.new(self).validate!
38
+ end
33
39
 
40
+ # Public: Called by ContentValidator if content is valid
41
+ #
42
+ def continue_write
34
43
  begin
35
- filepath = file_path_generator.output_filepath
36
- modified_content = modify_content_before_writing
37
- content_writer = Tobacco::Exhaler.new(modified_content, filepath)
38
-
44
+ content_writer = Tobacco::Exhaler.new(content, filepath)
39
45
  content_writer.write!
40
46
 
41
- callback(:on_success, modified_content)
47
+ Tobacco::Callback.instance.notify(:on_success, content)
42
48
 
43
49
  rescue => e
44
- Tobacco.log("ErrorWriting: #{filepath}")
45
-
46
- # Remove the empty file
47
- File.delete(filepath) if File.exists?(filepath)
48
-
49
- error = error_object('Error Writing', modified_content, e)
50
- callback(:on_write_error, error)
51
- end
52
- end
53
-
54
-
55
- #---------------------------------------------------------
56
- # End of Public API
57
- #---------------------------------------------------------
58
-
59
-
60
- #---------------------------------------------------------
61
- # Write helper methods
62
- #---------------------------------------------------------
63
- def modify_content_before_writing
64
- callback(:before_write, content)
65
- end
50
+ Tobacco.log("ErrorWriting: #{filepath}\nError: #{e}")
66
51
 
67
- #---------------------------------------------------------
68
- # Read helper methods
69
- #---------------------------------------------------------
70
- def read_content
71
- self.content = reader.send(Tobacco.content_method)
72
- end
73
-
74
- def verify_content
75
- unless content_present?
76
-
77
- # At this point, the content might be an error object
78
- # but if not, we create one
79
- #
80
- object = missing_content_error(content)
81
- error = error_object('Error Reading', '', object)
82
-
83
- callback(:on_read_error, error)
84
- end
85
- end
52
+ error = error_object('Error Writing', e)
53
+ Tobacco::Callback.instance.notify(:on_write_error, error)
86
54
 
87
- def missing_content_error(content)
88
- if content.respond_to? :message
89
- content
90
- else
91
- Tobacco::MissingContentError.new
55
+ # raise
92
56
  end
93
57
  end
94
58
 
95
- def content_present?
96
- @content_present ||= content?
97
- end
98
-
99
- def content?
100
- return false if content.nil? || content.empty?
101
-
102
- Array(content).last !~ /404 Not Found|The page you were looking for doesn't exist/
103
- end
104
-
105
- def choose_reader
106
- # The reader will either be the calling class (smoker)
107
- # if it provides the content method or a new Inhaler
108
- # object that will be used to read the content from a url
109
- #
110
- self.reader = \
111
- if smoker.respond_to? Tobacco.content_method
112
- smoker
113
- else
114
- Inhaler.new(file_path_generator.content_url).tap do |inhaler|
115
-
116
- # Add an alias for the user configured content_method
117
- # so that when it is called it calls :read
118
- # on the Inhaler instance
119
- #
120
- inhaler.instance_eval %{
121
- alias :"#{Tobacco.content_method}" :read
122
- }
123
- end
124
- end
59
+ def filepath
60
+ @filepath ||= file_path_generator.output_filepath
125
61
  end
126
62
 
127
63
  #---------------------------------------------------------
128
- # private
64
+ private
129
65
 
130
- def error_object(msg, modified_content, e)
66
+ def error_object(msg, e)
131
67
  Tobacco::Error.new(
132
68
  msg: msg,
133
- filepath: file_path_generator.output_filepath,
134
- content: modified_content,
69
+ filepath: filepath,
70
+ content: content,
135
71
  object: e
136
72
  )
137
73
  end
138
74
 
139
- def callback(name, error)
140
- if smoker.respond_to? name
141
- smoker.send(name, error)
142
- end
143
- end
144
75
  end
145
76
  end
@@ -1,3 +1,3 @@
1
1
  module Tobacco
2
- VERSION = "0.0.2"
2
+ VERSION = "0.0.3"
3
3
  end
data/lib/tobacco.rb CHANGED
@@ -13,13 +13,15 @@ module Tobacco
13
13
  @content_method = :content
14
14
  @content_url_method = :content_url
15
15
  @output_filepath_method = :output_filepath
16
+ @logging = false
16
17
 
17
18
  class << self
18
19
  attr_accessor :base_path,
19
20
  :published_host,
20
21
  :content_method,
21
22
  :content_url_method,
22
- :output_filepath_method
23
+ :output_filepath_method,
24
+ :logging
23
25
  end
24
26
 
25
27
  def self.configure
@@ -27,6 +29,8 @@ module Tobacco
27
29
  end
28
30
 
29
31
  def self.log(msg)
32
+ return unless logging
33
+
30
34
  log_msg = "\n*******************************************\n"
31
35
  log_msg += "Tobacco::Log: #{msg}\n"
32
36
  log_msg += "*******************************************\n"
@@ -45,4 +49,8 @@ require 'tobacco/roller'
45
49
  require 'tobacco/inhaler'
46
50
  require 'tobacco/exhaler'
47
51
  require 'tobacco/error'
52
+ require 'tobacco/safety_net'
53
+ require 'tobacco/content_validator'
54
+ require 'tobacco/content_reader'
55
+ require 'tobacco/callback'
48
56
 
@@ -0,0 +1,99 @@
1
+ require 'spec_helper'
2
+
3
+ describe Tobacco::ContentReader do
4
+ before do
5
+ Tobacco.configure do |config|
6
+ config.published_host = 'http://localhost:3000'
7
+ config.base_path = '/tmp/published_content'
8
+ config.content_method = :content
9
+ config.content_url_method = :content_url
10
+ config.output_filepath_method = :output_filepath
11
+ end
12
+ end
13
+
14
+ let(:consumer) { mock('consumer', content: 'Base content') }
15
+ let(:smoker) { mock('smoker', file_path_generator: filepath, consumer: consumer, content: '') }
16
+ let(:filepath) { mock('roller', content_url: '/video', output_filepath: '/file/path') }
17
+
18
+ subject { Tobacco::ContentReader.new(smoker) }
19
+
20
+ describe '#choose_reader' do
21
+ context 'when consumer provides the content' do
22
+ let(:consumer) { mock('consumer', content: 'Base content') }
23
+
24
+ it 'uses the consumer for the content' do
25
+ subject.choose_reader
26
+
27
+ subject.reader.should == consumer
28
+ end
29
+ end
30
+
31
+ context 'when smoker does not provide content' do
32
+ let(:inhaler) { mock('inhaler', read: 'reader method') }
33
+ let(:filepath) { mock('roller', content_url: '/video') }
34
+
35
+ before do
36
+ smoker.consumer.unstub!(:content)
37
+ Tobacco::Inhaler.stub(:new).and_return(inhaler)
38
+ end
39
+
40
+ it 'uses the smoker for the content' do
41
+ subject.choose_reader
42
+ subject.reader.should == inhaler
43
+ end
44
+ end
45
+ end
46
+
47
+
48
+ describe '#read_content' do
49
+ context 'when content is read from the consumer' do
50
+ let(:content) { 'Provided content' }
51
+
52
+ before { smoker.consumer.stub(:content).and_return(content) }
53
+
54
+ it 'should have the correct content' do
55
+ subject.choose_reader
56
+ subject.read_content
57
+
58
+ subject.content.should == content
59
+ end
60
+ end
61
+
62
+ context 'when content is read from url' do
63
+ let(:content) { 'Content from reading url' }
64
+ let(:inhaler) { mock('inhaler', read: content) }
65
+ let(:filepath) { mock('roller', content_url: '/video', output_filepath: '/tmp/howdy.txt') }
66
+
67
+ before do
68
+ smoker.consumer.unstub!(:content)
69
+ Tobacco::Inhaler.stub(:new).and_return(inhaler)
70
+ smoker.stub(:file_path_generator).and_return(filepath)
71
+ end
72
+
73
+ it 'should have the correct content' do
74
+ subject.choose_reader
75
+ subject.read
76
+
77
+ subject.content.should == content
78
+ end
79
+ end
80
+ end
81
+
82
+ describe 'modifies content' do
83
+ let(:content) { '<h1>Summer Gear</h1>' }
84
+ let(:modified_content) { '<h1>Winter Gear</h1>' }
85
+
86
+ before do
87
+ Tobacco::Callback.instance.writer = consumer
88
+ smoker.consumer.stub(:content).and_return(content)
89
+ smoker.consumer.stub(:before_write).and_return(modified_content)
90
+ end
91
+
92
+ it 'allows the consumer to modify content after reading' do
93
+ subject.read
94
+
95
+ subject.modified_content.should == modified_content
96
+ end
97
+ end
98
+
99
+ end
@@ -0,0 +1,53 @@
1
+ require 'spec_helper'
2
+
3
+
4
+ describe Tobacco::ContentValidator do
5
+
6
+ class Consumer
7
+ def on_read_error(error)
8
+ end
9
+ end
10
+
11
+ let(:consumer) { Consumer.new }
12
+ let(:smoker) { Tobacco::Smoker.new(consumer) }
13
+
14
+ subject { Tobacco::ContentValidator.new(smoker) }
15
+
16
+ before do
17
+ smoker.stub(:filepath).and_return('/path')
18
+ Tobacco::Callback.instance.stub(:writer).and_return(consumer)
19
+ end
20
+
21
+ describe '#validate!' do
22
+ context 'when validation passes' do
23
+ let(:content) { 'Lorem ipsum' }
24
+
25
+ before { smoker.content = content }
26
+
27
+ it 'calls continue write' do
28
+ smoker.should_receive(:continue_write)
29
+ smoker.consumer.should_not_receive(:on_read_error)
30
+
31
+ subject.validate!
32
+ end
33
+ end
34
+ end
35
+
36
+ describe '#validate!' do
37
+ context 'when validation fails' do
38
+ before { smoker.content = '' }
39
+
40
+ it 'does not call continue write on' do
41
+ smoker.should_not_receive(:continue_write)
42
+
43
+ subject.validate!
44
+ end
45
+
46
+ it 'calls on_read_error' do
47
+ consumer.should_receive(:on_read_error)
48
+
49
+ subject.validate!
50
+ end
51
+ end
52
+ end
53
+ end
@@ -43,6 +43,11 @@ describe Tobacco::Exhaler do
43
43
  let(:dir) { '/tmp/base_dir/' }
44
44
  let(:content) { '<h1>Page Title</h1>' }
45
45
 
46
+ before do
47
+ FileUtils.mkdir_p(dir)
48
+ File.open(filepath, 'w') { |f| f.write content }
49
+ end
50
+
46
51
  after { FileUtils.rm_rf(dir) }
47
52
 
48
53
  describe '#create_directory' do
@@ -0,0 +1,37 @@
1
+ require 'spec_helper'
2
+
3
+ describe Tobacco::SafetyNet do
4
+ let(:safety_net) { Tobacco::SafetyNet }
5
+ let(:filepath) { '/tmp/tempfile.txt' }
6
+ let(:content) { 'This little piggy went to market...' }
7
+
8
+ before do
9
+ File.open(filepath, 'w') {|f| f.write content }
10
+ @safety_net = safety_net.new(filepath)
11
+ @safety_net.backup
12
+ end
13
+
14
+ context 'when backing up' do
15
+ after { @safety_net.destroy }
16
+
17
+ it 'creates a tempfile' do
18
+ File.exists?(@safety_net.destination).should be_true
19
+ end
20
+
21
+ it 'has correct content' do
22
+ File.read(@safety_net.destination).should == content
23
+ end
24
+ end
25
+
26
+ context 'when restoring the original file' do
27
+ before { @safety_net.restore }
28
+
29
+ it 'works' do
30
+ File.exists?(filepath).should be_true
31
+ end
32
+
33
+ it 'has the correct content' do
34
+ File.read(filepath).should == content
35
+ end
36
+ end
37
+ end
@@ -1,7 +1,6 @@
1
1
  require 'spec_helper'
2
2
  require 'tobacco'
3
3
 
4
-
5
4
  describe Tobacco::Smoker do
6
5
  before do
7
6
  Tobacco.configure do |config|
@@ -13,22 +12,21 @@ describe Tobacco::Smoker do
13
12
  end
14
13
  end
15
14
 
16
- let(:smoker) { mock('smoker') }
15
+ let(:consumer) { mock('consumer') }
17
16
 
18
- subject { Tobacco::Smoker.new(smoker) }
17
+ subject { Tobacco::Smoker.new(consumer) }
19
18
 
20
19
  context '#write!' do
20
+ let(:filepath) { mock('roller', content_url: '/video', output_filepath: '/tmp/tobacco.txt') }
21
21
 
22
- context 'when content is empty' do
23
- let(:inhaler) { mock('inhaler', read: '') }
24
- let(:filepath) { mock('roller', content_url: '/video', output_filepath: '/user') }
22
+ before do
23
+ subject.file_path_generator = filepath
24
+ end
25
25
 
26
- before do
27
- Tobacco::Inhaler.stub(:new).and_return(inhaler)
28
- subject.file_path_generator = filepath
29
- end
26
+ context 'when content is empty' do
30
27
 
31
28
  it 'does not attempt a write' do
29
+ consumer.stub(:content).and_return('')
32
30
  Tobacco::Exhaler.should_not_receive(:new)
33
31
 
34
32
  subject.read
@@ -36,46 +34,44 @@ describe Tobacco::Smoker do
36
34
  end
37
35
  end
38
36
 
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', output_filepath: '/desktop') }
43
-
44
- before do
45
- Tobacco::Exhaler.stub(:new).and_return(exhaler)
46
- subject.file_path_generator = filepath
47
- end
37
+ context 'content success' do
38
+ let(:consumer) { mock('smoker') }
39
+ subject { Tobacco::Smoker.new(consumer) }
48
40
 
49
41
  context 'when providing content directly' do
42
+ let(:content) { 'Directly set content' }
43
+ let(:exhaler) { mock('exhaler', write!: true) }
44
+ let(:filepath) { mock('roller', content_url: '/video', output_filepath: '/tmp/tobacco.txt') }
50
45
 
51
- it 'allows setting content directly' do
52
- exhaler.should_receive(:write!)
46
+ before do
47
+ Tobacco::Exhaler.stub(:new).and_return(exhaler)
53
48
  subject.content = content
49
+ end
54
50
 
51
+ it 'uses the provided content' do
52
+ exhaler.should_receive(:write!)
55
53
  subject.write!
56
54
  end
57
55
 
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
56
+ it 'does not call the :before_write callback' do
57
+ Tobacco::Callback.instance.should_not_receive(:notify).with(:before_write, content)
58
+ subject.write!
59
+ end
62
60
 
61
+ it 'callback :on_success is called' do
62
+ Tobacco::Callback.instance.writer.should_receive(:on_success).with(content)
63
63
  subject.write!
64
64
  end
65
65
  end
66
+ end
66
67
 
68
+ context 'content errors' do
67
69
  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
70
+ let(:filepath) { mock('roller', content_url: '/video', output_filepath: '/users/blah.txt') }
75
71
 
76
72
  it 'calls the callback :on_write_error' do
77
- smoker.should_receive(:on_write_error)
78
- subject.content = content
73
+ Tobacco::Callback.instance.should_receive(:notify).once
74
+ subject.content = 'Directly set content'
79
75
 
80
76
  subject.write!
81
77
  end
@@ -83,122 +79,13 @@ describe Tobacco::Smoker do
83
79
  end
84
80
  end
85
81
 
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
82
  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.stub(: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) }
83
+ before { consumer.stub(:output_filepath).and_return('/tmp/path.txt') }
140
84
 
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.stub(: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', output_filepath: '/file/path') }
180
- let(:smoker) { Writer.new }
181
-
182
- before do
183
- Tobacco::Inhaler.stub(: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
85
+ it 'sets the file_path_generator' do
86
+ Tobacco::Roller.should_receive(:new).with(consumer)
198
87
 
199
- message.should match(/No error encountered/)
200
- end
201
- end
88
+ Tobacco::Smoker.new(consumer)
202
89
  end
203
90
  end
204
91
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: tobacco
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.2
4
+ version: 0.0.3
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-10-01 00:00:00.000000000 Z
12
+ date: 2012-10-08 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: rspec
@@ -108,19 +108,26 @@ files:
108
108
  - Rakefile
109
109
  - lib/tobacco.rb
110
110
  - lib/tobacco/burnout.rb
111
+ - lib/tobacco/callback.rb
112
+ - lib/tobacco/content_reader.rb
113
+ - lib/tobacco/content_validator.rb
111
114
  - lib/tobacco/error.rb
112
115
  - lib/tobacco/exhaler.rb
113
116
  - lib/tobacco/inhaler.rb
114
117
  - lib/tobacco/roller.rb
118
+ - lib/tobacco/safety_net.rb
115
119
  - lib/tobacco/smoker.rb
116
120
  - lib/tobacco/version.rb
117
121
  - spec/spec_helper.rb
118
122
  - spec/support/vcr_cassettes/url_content.yml
119
123
  - spec/tobacco/burnout_spec.rb
124
+ - spec/tobacco/content_reader_spec.rb
125
+ - spec/tobacco/content_validator_spec.rb
120
126
  - spec/tobacco/error_spec.rb
121
127
  - spec/tobacco/exhaler_spec.rb
122
128
  - spec/tobacco/inhaler_spec.rb
123
129
  - spec/tobacco/roller_spec.rb
130
+ - spec/tobacco/safety_net_spec.rb
124
131
  - spec/tobacco/smoker_spec.rb
125
132
  - spec/tobacco_spec.rb
126
133
  - tobacco.gemspec
@@ -152,9 +159,12 @@ test_files:
152
159
  - spec/spec_helper.rb
153
160
  - spec/support/vcr_cassettes/url_content.yml
154
161
  - spec/tobacco/burnout_spec.rb
162
+ - spec/tobacco/content_reader_spec.rb
163
+ - spec/tobacco/content_validator_spec.rb
155
164
  - spec/tobacco/error_spec.rb
156
165
  - spec/tobacco/exhaler_spec.rb
157
166
  - spec/tobacco/inhaler_spec.rb
158
167
  - spec/tobacco/roller_spec.rb
168
+ - spec/tobacco/safety_net_spec.rb
159
169
  - spec/tobacco/smoker_spec.rb
160
170
  - spec/tobacco_spec.rb