mint 0.2.9 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -10,21 +10,19 @@ module Mint
10
10
  # We do have to test #style_destination derivatives. Those aren't
11
11
  # covered by resource_spec.rb.
12
12
  shared_examples_for "all documents" do
13
- # Convenience methods
14
-
15
- it "#stylesheet" do
16
- document.stylesheet.should ==
17
- Helpers.normalize_path(document.style_destination_file,
18
- document.destination_directory).to_s
19
- end
20
-
21
13
  # style_spec.rb ensures that our style generation goes as planned
22
14
  # However, we need to test layout generation because it should now
23
15
  # include our content
16
+ #
17
+ # This test doesn't cover any plugin transformations. Those
18
+ # transformations are covered in the Plugin spec.
24
19
  its(:content) { should =~ /<p>This is just a test.<\/p>/ }
20
+ its(:metadata) { should == { 'metadata' => true } }
25
21
 
26
22
  # Render output
27
-
23
+
24
+ # This test doesn't cover any plugin transformations. Those
25
+ # transformations are covered in the Plugin spec.
28
26
  it "renders its layout, injecting content inside" do
29
27
  document.render.should =~
30
28
  /.*<html>.*#{document.content}.*<\/html>.*/m
@@ -36,6 +34,8 @@ module Mint
36
34
 
37
35
  # Mint output
38
36
 
37
+ # These tests doesn't cover any plugin transformations. Those
38
+ # transformations are covered in the Plugin spec.
39
39
  it "writes its rendered style to #style_destination_file" do
40
40
  document.publish!
41
41
  document.style_destination_file_path.should exist
@@ -77,6 +77,8 @@ module Mint
77
77
  its(:layout) { should be_in_directory('default') }
78
78
  its(:style) { should be_in_directory('default') }
79
79
 
80
+ its(:stylesheet) { should == Mint.root + '/templates/default/css/style.css' }
81
+
80
82
  it_should_behave_like "all documents"
81
83
  end
82
84
 
@@ -110,6 +112,8 @@ module Mint
110
112
  its(:layout) { should be_in_directory('default') }
111
113
  its(:style) { should be_in_directory('default') }
112
114
 
115
+ its(:stylesheet) { should == 'styles/style.css' }
116
+
113
117
  it_should_behave_like "all documents"
114
118
  end
115
119
 
@@ -142,6 +146,8 @@ module Mint
142
146
  its(:layout) { should be_in_directory('default') }
143
147
  its(:style) { should be_in_directory('default') }
144
148
 
149
+ its(:stylesheet) { should == Mint.root + '/templates/default/css/style.css' }
150
+
145
151
  it_should_behave_like "all documents"
146
152
  end
147
153
 
@@ -181,7 +187,40 @@ module Mint
181
187
  its(:layout) { should be_in_directory('pro') }
182
188
  its(:style) { should be_in_directory('pro') }
183
189
 
190
+ its(:stylesheet) { should == 'styles/style.css' }
191
+
184
192
  it_should_behave_like "all documents"
185
193
  end
194
+
195
+ context "when dealing with metadata" do
196
+ let(:text) { "metadata: true\n\nReal text" }
197
+ describe ".metadata_chunk" do
198
+ it "extracts, but does not parse, metadata from text" do
199
+ Document.metadata_chunk(text).should == 'metadata: true'
200
+ end
201
+ end
202
+
203
+ describe ".metadata_from" do
204
+ it "parses a documents metadata if present" do
205
+ Document.metadata_from(text).should == { 'metadata' => true }
206
+ end
207
+
208
+ it "returns the empty string if a document has bad/no metadata" do
209
+ Document.metadata_from('No metadata here').should == {}
210
+ end
211
+ end
212
+
213
+ describe ".parse_metadata_from" do
214
+ it "separates text from its metadata if present" do
215
+ Document.parse_metadata_from(text).should ==
216
+ [{ 'metadata' => true }, 'Real text']
217
+ end
218
+
219
+ it "returns the entire text if no metadata is found" do
220
+ Document.parse_metadata_from('No metadata here').should ==
221
+ [{}, 'No metadata here']
222
+ end
223
+ end
224
+ end
186
225
  end
187
226
  end
@@ -0,0 +1,231 @@
1
+ require 'spec_helper'
2
+
3
+ module Mint
4
+ describe Helpers do
5
+ describe "#underscore" do
6
+ it "underscores class names per ActiveSupport conventions" do
7
+ Helpers.underscore('ClassName').should == 'class_name'
8
+ end
9
+
10
+ it "allows for camel case prefixes" do
11
+ Helpers.underscore('EPub').should == 'e_pub'
12
+ Helpers.underscore('EPub', :ignore_prefix => true).should == 'epub'
13
+ end
14
+
15
+ it "allows for namespace removal" do
16
+ Helpers.underscore('Mint::EPub',
17
+ :namespaces => true).should == 'mint/e_pub'
18
+ Helpers.underscore('Mint::EPub',
19
+ :namespaces => false).should == 'e_pub'
20
+ Helpers.underscore('Mint::EPub',
21
+ :namespaces => true,
22
+ :ignore_prefix => true).should == 'mint/epub'
23
+ end
24
+ end
25
+
26
+ describe "#slugize" do
27
+ it "downcases everything" do
28
+ Helpers.slugize('This could use FEWER CAPITALS').should ==
29
+ 'this-could-use-fewer-capitals'
30
+ end
31
+
32
+ it "parses 'and'" do
33
+ Helpers.slugize('You & me').should == 'you-and-me'
34
+ end
35
+
36
+ it "parses spaces" do
37
+ Helpers.slugize('You and me').should == 'you-and-me'
38
+ end
39
+
40
+ it "removes non-word/non-digits" do
41
+ Helpers.slugize('You // and :: me').should == 'you-and-me'
42
+ end
43
+
44
+ it "condenses multiple hyphens" do
45
+ Helpers.slugize('You-----and me').should == 'you-and-me'
46
+ end
47
+ end
48
+
49
+ describe "#symbolize" do
50
+ it "converts hyphens to underscores" do
51
+ Helpers.symbolize('you-and-me').should == :you_and_me
52
+ end
53
+ end
54
+
55
+ describe "#pathize" do
56
+ it "converts a String to a Pathname" do
57
+ Helpers.pathize("filename.md").should ==
58
+ Pathname.new("filename.md").expand_path
59
+ end
60
+
61
+ it "does not convert a Pathname" do
62
+ pathname = Pathname.new("filename.md")
63
+ Helpers.pathize(pathname).should == pathname.expand_path
64
+ end
65
+ end
66
+
67
+ describe "#symbolize_keys" do
68
+ it "turns all string keys in a flat map into symbols" do
69
+ flat_map = {
70
+ 'key1' => 'value1',
71
+ 'key2' => 'value2',
72
+ 'key3' => 'value3'
73
+ }
74
+
75
+ expected_map = {
76
+ key1: 'value1',
77
+ key2: 'value2',
78
+ key3: 'value3'
79
+ }
80
+
81
+ Helpers.symbolize_keys(flat_map).should == expected_map
82
+ end
83
+
84
+ it "recursively turns all string keys in a nested map into symbols" do
85
+ nested_map = {
86
+ 'key1' => 'value1',
87
+ 'key2' => 'value2',
88
+ 'key3' => 'value3',
89
+ 'key4' => {
90
+ 'nested_key1' => 'nested_value1',
91
+ 'nested_key2' => 'nested_value2'
92
+ }
93
+ }
94
+
95
+ expected_map = {
96
+ key1: 'value1',
97
+ key2: 'value2',
98
+ key3: 'value3',
99
+ key4: {
100
+ nested_key1: 'nested_value1',
101
+ nested_key2: 'nested_value2'
102
+ }
103
+ }
104
+
105
+ Helpers.symbolize_keys(nested_map).should == expected_map
106
+ end
107
+
108
+ it "recursively downcases all keys if specified" do
109
+ capitalized_map = {
110
+ 'Key1' => 'value1',
111
+ 'Key2' => 'value2',
112
+ 'Key3' => 'value3',
113
+ 'Key4' => {
114
+ 'Nested_key1' => 'nested_value1',
115
+ 'Nested_key2' => 'nested_value2'
116
+ }
117
+ }
118
+
119
+ expected_map = {
120
+ key1: 'value1',
121
+ key2: 'value2',
122
+ key3: 'value3',
123
+ key4: {
124
+ nested_key1: 'nested_value1',
125
+ nested_key2: 'nested_value2'
126
+ }
127
+ }
128
+
129
+ Helpers.symbolize_keys(capitalized_map, :downcase => true).should == expected_map
130
+ end
131
+ end
132
+
133
+ describe "#listify" do
134
+ it "joins a list of three or more with an ampersand, without the Oxford comma" do
135
+ Helpers.listify(['Alex', 'Chris', 'John']).should ==
136
+ 'Alex, Chris & John'
137
+ end
138
+
139
+ it "joins a list of two with an ampersand" do
140
+ Helpers.listify(['Alex', 'Chris']).should == 'Alex & Chris'
141
+ end
142
+
143
+ it "does not do anything to a list of one" do
144
+ Helpers.listify(['Alex']).should == 'Alex'
145
+ end
146
+ end
147
+
148
+ describe "#standardize" do
149
+ before do
150
+ @nonstandard = {
151
+ title: 'Title',
152
+ author: 'David',
153
+ editors: ['David', 'Jake'],
154
+ barcode: 'Unique ID'
155
+ }
156
+
157
+ @table = {
158
+ author: [:creators, :array],
159
+ editors: [:collaborators, :array],
160
+ barcode: [:uuid, :string]
161
+ }
162
+
163
+ @standard = {
164
+ title: 'Title',
165
+ creators: ['David'],
166
+ collaborators: ['David', 'Jake'],
167
+ uuid: 'Unique ID'
168
+ }
169
+ end
170
+
171
+ it "converts all nonstandard keys to standard ones" do
172
+ Helpers.standardize(@nonstandard,
173
+ :table => @table).should == @standard
174
+ end
175
+ end
176
+
177
+ describe "#normalize_path" do
178
+ it "handles two files in the same directory" do
179
+ path1 = '~/file1'
180
+ path2 = '~/file2'
181
+ Helpers.normalize_path(path1, path2).should ==
182
+ Pathname.new('../file1')
183
+ end
184
+
185
+ it "handles two files one directory apart" do
186
+ path1 = '~/file1'
187
+ path2 = '~/subdir/file2'
188
+ Helpers.normalize_path(path1, path2).should ==
189
+ Pathname.new('../../file1')
190
+ end
191
+
192
+ it "handles two files linked only at the directory root" do
193
+ path1 = '/home/david/file1'
194
+ path2 = '/usr/local/src/file2'
195
+ Helpers.normalize_path(path1, path2).should ==
196
+ Pathname.new('/home/david/file1')
197
+ end
198
+
199
+ it "returns nil for identical files" do
200
+ path1 = '~/file1'
201
+ path2 = '~/file1'
202
+ Helpers.normalize_path(path1, path2).should == Pathname.new('.')
203
+ end
204
+ end
205
+
206
+ describe "#update_yaml!" do
207
+ it "loads existing YAML data from file"
208
+ it "combines existing YAML data with new data and writes to file"
209
+ end
210
+
211
+ describe "#generate_temp_file!" do
212
+ before do
213
+ @file = Helpers.generate_temp_file! 'content.md'
214
+ @path = Pathname.new @file
215
+ end
216
+
217
+ it "creates a randomly named temp file" do
218
+ @path.should exist
219
+ end
220
+
221
+ it "creates a temp file with the correct name and extension" do
222
+ @path.basename.to_s.should =~ /content/
223
+ @path.extname.should == '.md'
224
+ end
225
+
226
+ it "fills the temp file with the specified content" do
227
+ @path.read.should =~ /This is just a test/
228
+ end
229
+ end
230
+ end
231
+ end
@@ -0,0 +1,6 @@
1
+ require 'spec_helper'
2
+
3
+ module Mint
4
+ describe Layout do
5
+ end
6
+ end
@@ -0,0 +1,94 @@
1
+ require 'spec_helper'
2
+
3
+ describe Mint do
4
+ subject { Mint }
5
+ its(:root) { should == File.expand_path('../../../mint', __FILE__) }
6
+ its(:path) { should == ["#{Dir.getwd}/.mint", "~/.mint", Mint.root] }
7
+ its(:formats) { should include('md') }
8
+ its(:css_formats) { should include('sass') }
9
+ its(:templates) { should include(Mint.root + '/templates/default') }
10
+
11
+ its(:default_options) do
12
+ should == {
13
+ layout: 'default',
14
+ style: 'default',
15
+ destination: nil,
16
+ style_destination: nil
17
+ }
18
+ end
19
+
20
+ its(:directories) do
21
+ should == {
22
+ templates: 'templates',
23
+ config: 'config'
24
+ }
25
+ end
26
+
27
+ its(:files) do
28
+ should == {
29
+ syntax: Mint.directories[:config] + '/syntax.yaml',
30
+ config: Mint.directories[:config] + '/config.yaml'
31
+ }
32
+ end
33
+
34
+ it "creates a valid renderer" do
35
+ Mint.renderer(@content_file).should respond_to(:render)
36
+ end
37
+
38
+ it "chooses the appropriate path for scope" do
39
+ Mint.path_for_scope(:local).should == "#{Dir.getwd}/.mint"
40
+ Mint.path_for_scope(:user).should == '~/.mint'
41
+ Mint.path_for_scope(:global).should == Mint.root
42
+ end
43
+
44
+ it "looks up the correct template according to scope" do
45
+ Mint.lookup_template(:default, :layout).should be_in_template('default')
46
+ Mint.lookup_template(:default, :style).should be_in_template('default')
47
+ Mint.lookup_template(:pro, :layout).should be_in_template('pro')
48
+ Mint.lookup_template(:pro, :style).should be_in_template('pro')
49
+ Mint.lookup_template('layout.haml').should == 'layout.haml'
50
+ Mint.lookup_template('dynamic.sass').should == 'dynamic.sass'
51
+ end
52
+
53
+ it "finds the correct template according to scope" do
54
+ Mint.find_template('default', :layout).should be_in_template('default')
55
+ Mint.find_template('pro', :layout).should be_in_template('pro')
56
+ Mint.find_template('pro', :style).should be_in_template('pro')
57
+ end
58
+
59
+ it "decides whether or not a file is a template file" do
60
+ actual_template = Mint.lookup_template(:default, :layout)
61
+ fake_template = "#{Mint.root}/templates/default.css"
62
+ obvious_nontemplate = @dynamic_style_file
63
+
64
+ actual_template.should be_a_template
65
+ fake_template.should_not be_a_template
66
+ obvious_nontemplate.should_not be_a_template
67
+ end
68
+
69
+ it "properly guesses destination file names based on source file names" do
70
+ Mint.guess_name_from('content.md').should == 'content.html'
71
+ Mint.guess_name_from('content.textile').should == 'content.html'
72
+ Mint.guess_name_from('layout.haml').should == 'layout.html'
73
+ Mint.guess_name_from('dynamic.sass').should == 'dynamic.css'
74
+ end
75
+
76
+ context "before it publishes a document" do
77
+ let(:document) { Mint::Document.new @content_file }
78
+ subject { document }
79
+
80
+ its(:destination_file_path) { should_not exist }
81
+ its(:style_destination_file_path) { should_not exist }
82
+ end
83
+
84
+ # These are copied from document_spec.rb. I eventually want to move
85
+ # to this non-OO style of publishing, and this is the transition
86
+ context "when it publishes a document" do
87
+ let(:document) { Mint::Document.new @content_file }
88
+ before { Mint.publish! document }
89
+ subject { document }
90
+
91
+ its(:destination_file_path) { should exist }
92
+ its(:style_destination_file_path) { should exist }
93
+ end
94
+ end
@@ -0,0 +1,457 @@
1
+ require 'spec_helper'
2
+
3
+ describe Mint do
4
+ # Remove unintended side effects of creating
5
+ # new plugins in other files.
6
+ before { Mint.clear_plugins! }
7
+ after { Mint.clear_plugins! }
8
+
9
+ describe ".plugins" do
10
+ it "returns all registered plugins" do
11
+ plugin = Class.new(Mint::Plugin)
12
+ Mint.plugins.should == [plugin]
13
+ end
14
+
15
+ it "returns an empty array if there are no registered plugins" do
16
+ Mint.plugins.should == []
17
+ end
18
+ end
19
+
20
+ describe ".activated_plugins" do
21
+ it "returns a list of plugins activated for a document"
22
+ it "returns a list of plugins activated for a set of documents"
23
+ it "returns a list of plugins activated for all documents"
24
+ end
25
+
26
+ describe ".register_plugin!" do
27
+ let(:plugin) { Class.new }
28
+
29
+ it "registers a plugin once" do
30
+ Mint.register_plugin! plugin
31
+ Mint.plugins.should == [plugin]
32
+ end
33
+
34
+ it "does not register a plugin more than once" do
35
+ Mint.register_plugin! plugin
36
+ lambda { Mint.register_plugin! plugin }.should_not change { Mint.plugins }
37
+ Mint.plugins.should == [plugin]
38
+ end
39
+ end
40
+
41
+ describe ".activate_plugin!" do
42
+ let(:plugin) { Class.new }
43
+
44
+ it "activates a plugin once" do
45
+ Mint.activate_plugin! plugin
46
+ Mint.activated_plugins.should == [plugin]
47
+ end
48
+
49
+ it "does not register a plugin more than once" do
50
+ Mint.activate_plugin! plugin
51
+ lambda { Mint.activate_plugin! plugin }.should_not change { Mint.activated_plugins }
52
+ Mint.activated_plugins.should == [plugin]
53
+ end
54
+ end
55
+
56
+ describe ".clear_plugins!" do
57
+ let(:plugin) { Class.new }
58
+
59
+ it "does nothing if no plugins are registered" do
60
+ lambda { Mint.clear_plugins! }.should_not raise_error
61
+ end
62
+
63
+ it "removes all registered plugins" do
64
+ Mint.register_plugin! plugin
65
+ lambda { Mint.clear_plugins! }.should change { Mint.plugins.length }.by(-1)
66
+ end
67
+
68
+ it "removes all activated plugins" do
69
+ Mint.activate_plugin! plugin
70
+ lambda { Mint.clear_plugins! }.should change { Mint.activated_plugins.length }.by(-1)
71
+ end
72
+ end
73
+
74
+ describe ".template_directory" do
75
+ let(:plugin) { Class.new(Mint::Plugin) }
76
+
77
+ it "gives access to a directory where template files can be stored" do
78
+ plugin.should_receive(:name).and_return('DocBook')
79
+ Mint.template_directory(plugin).should ==
80
+ Mint.root + '/plugins/templates/doc_book'
81
+ end
82
+ end
83
+
84
+ describe ".config_directory" do
85
+ let(:plugin) { Class.new(Mint::Plugin) }
86
+
87
+ it "gives access to a directory where template files can be stored" do
88
+ plugin.should_receive(:name).and_return('DocBook')
89
+ Mint.config_directory(plugin).should ==
90
+ Mint.root + '/plugins/config/doc_book'
91
+ end
92
+ end
93
+
94
+ describe ".commandline_options_file" do
95
+ let(:plugin) { Class.new(Mint::Plugin) }
96
+
97
+ it "gives access to a directory where template files can be stored" do
98
+ plugin.should_receive(:name).and_return('DocBook')
99
+ Mint.commandline_options_file(plugin).should ==
100
+ Mint.root + '/plugins/config/doc_book/syntax.yml'
101
+ end
102
+ end
103
+
104
+ [:before_render, :after_render].each do |callback|
105
+ describe ".#{callback}" do
106
+ let(:first_plugin) { Class.new(Mint::Plugin) }
107
+ let(:second_plugin) { Class.new(Mint::Plugin) }
108
+ let(:third_plugin) { Class.new(Mint::Plugin) }
109
+
110
+ context "when plugins are specified" do
111
+ before do
112
+ first_plugin.should_receive(callback).ordered.and_return('first')
113
+ second_plugin.should_receive(callback).ordered.and_return('second')
114
+ third_plugin.should_receive(callback).never
115
+ end
116
+
117
+ it "reduces .#{callback} across all specified plugins in order" do
118
+ plugins = [first_plugin, second_plugin]
119
+ Mint.send(callback, 'text', :plugins => plugins).should == 'second'
120
+ end
121
+ end
122
+
123
+ context "when plugins are activated, but no plugins are specified" do
124
+ before do
125
+ first_plugin.should_receive(callback).ordered.and_return('first')
126
+ second_plugin.should_receive(callback).ordered.and_return('second')
127
+ third_plugin.should_receive(callback).never
128
+ end
129
+
130
+ it "reduces .#{callback} across all activated plugins in order" do
131
+ Mint.activate_plugin! first_plugin
132
+ Mint.activate_plugin! second_plugin
133
+ Mint.send(callback, 'text').should == 'second'
134
+ end
135
+ end
136
+
137
+ context "when plugins are not specified" do
138
+ before do
139
+ first_plugin.should_receive(callback).never
140
+ second_plugin.should_receive(callback).never
141
+ third_plugin.should_receive(callback).never
142
+ end
143
+
144
+ it "returns the parameter text" do
145
+ Mint.send(callback, 'text').should == 'text'
146
+ end
147
+ end
148
+ end
149
+ end
150
+
151
+ describe ".after_publish" do
152
+ let(:first_plugin) { Class.new(Mint::Plugin) }
153
+ let(:second_plugin) { Class.new(Mint::Plugin) }
154
+ let(:third_plugin) { Class.new(Mint::Plugin) }
155
+
156
+ context "when plugins are specified" do
157
+ before do
158
+ first_plugin.should_receive(:after_publish).ordered
159
+ second_plugin.should_receive(:after_publish).ordered
160
+ third_plugin.should_receive(:after_publish).never
161
+ end
162
+
163
+ it "iterates across all specified plugins in order" do
164
+ plugins = [first_plugin, second_plugin]
165
+ Mint.after_publish('fake document', :plugins => plugins)
166
+ end
167
+ end
168
+
169
+ context "when plugins are activated, but no plugins are specified" do
170
+ before do
171
+ first_plugin.should_receive(:after_publish).ordered
172
+ second_plugin.should_receive(:after_publish).ordered
173
+ third_plugin.should_receive(:after_publish).never
174
+ end
175
+
176
+ it "iterates across all activated plugins in order" do
177
+ Mint.activate_plugin! first_plugin
178
+ Mint.activate_plugin! second_plugin
179
+ Mint.after_publish('fake document')
180
+ end
181
+ end
182
+
183
+ context "when plugins are not specified" do
184
+ before do
185
+ first_plugin.should_receive(:after_publish).never
186
+ second_plugin.should_receive(:after_publish).never
187
+ third_plugin.should_receive(:after_publish).never
188
+ end
189
+
190
+ it "does not iterate over any plugins" do
191
+ Mint.after_publish('fake document')
192
+ end
193
+ end
194
+ end
195
+
196
+ # TODO: Document expected document functionality changes related to plugins
197
+ describe Mint::Document do
198
+ context "when plugins are registered with Mint" do
199
+ describe "#content=" do
200
+ it "applies each registered plugin's before_render filter"
201
+ end
202
+
203
+ describe "#render" do
204
+ it "applies each registered plugin's after_render filter"
205
+ end
206
+
207
+ describe "#publish!" do
208
+ it "applies each registered plugin's after_publish filter"
209
+ end
210
+ end
211
+ end
212
+
213
+ describe Mint::Plugin do
214
+ # We have to instantiate these plugins in a before block,
215
+ # and not in a let block. Because lets are lazily evaluated,
216
+ # the first two tests in the '#inherited' suite will not
217
+ # pass.
218
+ before do
219
+ @first_plugin = Class.new(Mint::Plugin)
220
+ @second_plugin = Class.new(Mint::Plugin)
221
+ end
222
+
223
+ describe ".underscore" do
224
+ let(:plugin) { Class.new(Mint::Plugin) }
225
+
226
+ it "when anonymous, returns a random identifier"
227
+
228
+ it "when named, returns its name, underscored" do
229
+ plugin.should_receive(:name).and_return('EPub')
230
+ plugin.underscore.should == 'epub'
231
+ end
232
+ end
233
+
234
+ describe ".inherited" do
235
+ it "registers the subclass with Mint as a plugin" do
236
+ lambda do
237
+ Class.new(Mint::Plugin)
238
+ end.should change { Mint.plugins.length }.by(1)
239
+ end
240
+
241
+ it "preserves the order of subclassing" do
242
+ Mint.plugins.should == [@first_plugin, @second_plugin]
243
+ end
244
+
245
+ it "does not change the order of a plugin when it is monkey-patched" do
246
+ lambda do
247
+ @first_plugin.instance_eval do
248
+ def monkey_patch
249
+ end
250
+ end
251
+ end.should_not change { Mint.plugins }
252
+ end
253
+ end
254
+
255
+ describe ".commandline_options" do
256
+ let(:plugin) { Class.new(Mint::Plugin) }
257
+ before do
258
+ plugin.instance_eval do
259
+ def commandline_options
260
+ end
261
+ end
262
+ end
263
+
264
+ it "returns a hash of options the plugin can take, including constraints"
265
+ end
266
+
267
+ context "plugin callbacks" do
268
+ let(:plugin) { Class.new(Mint::Plugin) }
269
+
270
+ describe ".before_render" do
271
+ it "allows changes to the un-rendered content" do
272
+ plugin.instance_eval do
273
+ def before_render(text_document)
274
+ 'base'
275
+ end
276
+ end
277
+
278
+ plugin.before_render('text').should == 'base'
279
+ end
280
+ end
281
+
282
+ describe ".after_render" do
283
+ it "allows changes to the rendered HTML" do
284
+ plugin.instance_eval do
285
+ def after_render(html_document)
286
+ '<!doctype html>'
287
+ end
288
+ end
289
+
290
+ plugin.after_render('<html></html>').should == '<!doctype html>'
291
+ end
292
+ end
293
+
294
+ describe ".after_mint" do
295
+ let(:document) { Mint::Document.new 'content.md' }
296
+
297
+ it "allows changes to the document extension" do
298
+ plugin.instance_eval do
299
+ def after_publish(document)
300
+ document.name.gsub! /html$/, 'htm'
301
+ end
302
+ end
303
+
304
+ lambda do
305
+ plugin.after_publish(document)
306
+ end.should change { document.name.length }.by(-1)
307
+ end
308
+
309
+ it "allows splitting up the document into two, without garbage" do
310
+ plugin.instance_eval do
311
+ def after_publish(document)
312
+ content = document.content
313
+ fake_splitting_point = content.length / 2
314
+
315
+ first = content[0..fake_splitting_point]
316
+ second = content[fake_splitting_point..-1]
317
+
318
+ File.open 'first-half.html', 'w+' do |file|
319
+ file << first
320
+ end
321
+
322
+ File.open 'second-half.html', 'w+' do |file|
323
+ file << second
324
+ end
325
+
326
+ File.delete document.destination_file
327
+ end
328
+ end
329
+
330
+ document.publish! :plugins => [plugin]
331
+
332
+ File.exist?(document.destination_file).should be_false
333
+ File.exist?('first-half.html').should be_true
334
+ File.exist?('second-half.html').should be_true
335
+ end
336
+
337
+ it "allows changes to the style file" do
338
+ pending "figure out a better strategy for style manipulation"
339
+ document = Mint::Document.new 'content.md', :style => 'style.css'
340
+
341
+ plugin.instance_eval do
342
+ def after_publish(document)
343
+ # I'd like to take document.style_destination_file,
344
+ # but the current Mint API doesn't allow for this
345
+ # if we're setting the style via a concrete
346
+ # stylesheet in our current directory
347
+ style_source = document.style.source_file
348
+ style = File.read style_source
349
+ File.open style_source, 'w' do |file|
350
+ file << style.gsub(/#/, '.')
351
+ end
352
+ end
353
+ end
354
+
355
+ document.publish! :plugins => [plugin]
356
+
357
+ File.read(document.style.source_file).should =~ /\#container/
358
+ end
359
+
360
+ context "when the output is in the default directory" do
361
+ it "doesn't allow changes to the document directory" do
362
+ pending "figuring out the best way to prevent directory manipulation"
363
+ document = Mint::Document.new 'content.md'
364
+ plugin.instance_eval do
365
+ def after_publish
366
+ original = document.destination_directory
367
+ new = File.expand_path 'invalid'
368
+ FileUtils.mv original, new
369
+ document.destination = 'invalid'
370
+ end
371
+
372
+ lambda do
373
+ document.publish! :plugins => [plugin]
374
+ end.should raise_error(InvalidPluginAction)
375
+ end
376
+ end
377
+ end
378
+
379
+ context "when the output is a new directory" do
380
+ it "allows changes to the document directory" do
381
+ document = Mint::Document.new 'content.md', :destination => 'destination'
382
+ plugin.instance_eval do
383
+ def after_publish(document)
384
+ original = document.destination_directory
385
+ new = File.expand_path 'book'
386
+ FileUtils.mv original, new
387
+ document.destination = 'book'
388
+ end
389
+ end
390
+
391
+ document.publish! :plugins => [plugin]
392
+ File.exist?('destination').should be_false
393
+ File.exist?('book').should be_true
394
+ document.destination_directory.should == File.expand_path('book')
395
+ end
396
+
397
+ it "allows compression of the final output" do
398
+ require 'zip/zip'
399
+ require 'zip/zipfilesystem'
400
+
401
+ document = Mint::Document.new 'content.md', :destination => 'destination'
402
+ plugin.instance_eval do
403
+ def after_publish(document)
404
+ Zip::ZipOutputStream.open 'book.zip' do |zos|
405
+ # zos.put_next_entry('mimetype', nil, nil, Zip::ZipEntry::STORED)
406
+ # zos.puts 'text/epub'
407
+ zos.put_next_entry('chapter-1', nil, nil, Zip::ZipEntry::DEFLATED)
408
+ zos.puts File.read(document.destination_file)
409
+ end
410
+
411
+ FileUtils.mv 'book.zip', 'book.epub'
412
+ end
413
+ end
414
+
415
+ document.publish! :plugins => [plugin]
416
+
417
+ File.exist?('destination').should be_true
418
+ File.exist?('book.zip').should be_false
419
+ File.exist?('book.epub').should be_true
420
+
421
+ directory_size =
422
+ Dir["#{document.destination_directory}/**/*"].
423
+ flatten.
424
+ map {|file| File.stat(file).size }.
425
+ reduce(&:+)
426
+ compressed_size = File.stat('book.epub').size
427
+ directory_size.should > compressed_size
428
+ end
429
+ end
430
+
431
+ context "when the style output is a new directory" do
432
+ it "allows changes to the style directory" do
433
+ document = Mint::Document.new 'content.md', :style_destination => 'styles'
434
+ plugin.instance_eval do
435
+ def after_publish(document)
436
+ original = document.style_destination_directory
437
+ new = File.expand_path 'looks'
438
+ FileUtils.mv original, new
439
+ document.style_destination = 'looks'
440
+ end
441
+ end
442
+
443
+ document.publish! :plugins => [plugin]
444
+
445
+ File.exist?('styles').should be_false
446
+ File.exist?('looks').should be_true
447
+ document.style_destination_directory.should == File.expand_path('looks')
448
+ end
449
+
450
+ after do
451
+ FileUtils.rm_r File.expand_path('looks')
452
+ end
453
+ end
454
+ end
455
+ end
456
+ end
457
+ end