bluecloth 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (103) hide show
  1. data/ChangeLog +629 -0
  2. data/LICENSE +27 -0
  3. data/LICENSE.discount +47 -0
  4. data/README +71 -0
  5. data/Rakefile +319 -0
  6. data/Rakefile.local +63 -0
  7. data/bin/bluecloth +84 -0
  8. data/ext/VERSION +1 -0
  9. data/ext/amalloc.h +29 -0
  10. data/ext/bluecloth.c +373 -0
  11. data/ext/config.h +47 -0
  12. data/ext/cstring.h +73 -0
  13. data/ext/docheader.c +43 -0
  14. data/ext/extconf.rb +45 -0
  15. data/ext/generate.c +1387 -0
  16. data/ext/markdown.c +939 -0
  17. data/ext/markdown.h +135 -0
  18. data/ext/mkdio.c +241 -0
  19. data/ext/mkdio.h +66 -0
  20. data/ext/resource.c +169 -0
  21. data/ext/version.c +28 -0
  22. data/lib/bluecloth.rb +148 -0
  23. data/rake/191_compat.rb +26 -0
  24. data/rake/dependencies.rb +76 -0
  25. data/rake/helpers.rb +412 -0
  26. data/rake/manual.rb +782 -0
  27. data/rake/packaging.rb +116 -0
  28. data/rake/publishing.rb +321 -0
  29. data/rake/rdoc.rb +40 -0
  30. data/rake/style.rb +62 -0
  31. data/rake/svn.rb +639 -0
  32. data/rake/testing.rb +204 -0
  33. data/rake/verifytask.rb +64 -0
  34. data/rake/win32.rb +186 -0
  35. data/spec/bluecloth/101_changes_spec.rb +141 -0
  36. data/spec/bluecloth/autolinks_spec.rb +49 -0
  37. data/spec/bluecloth/blockquotes_spec.rb +143 -0
  38. data/spec/bluecloth/code_spans_spec.rb +164 -0
  39. data/spec/bluecloth/emphasis_spec.rb +164 -0
  40. data/spec/bluecloth/entities_spec.rb +65 -0
  41. data/spec/bluecloth/hrules_spec.rb +90 -0
  42. data/spec/bluecloth/images_spec.rb +92 -0
  43. data/spec/bluecloth/inline_html_spec.rb +238 -0
  44. data/spec/bluecloth/links_spec.rb +171 -0
  45. data/spec/bluecloth/lists_spec.rb +294 -0
  46. data/spec/bluecloth/paragraphs_spec.rb +75 -0
  47. data/spec/bluecloth/titles_spec.rb +305 -0
  48. data/spec/bluecloth_spec.rb +209 -0
  49. data/spec/bugfix_spec.rb +123 -0
  50. data/spec/contributions_spec.rb +85 -0
  51. data/spec/data/antsugar.txt +34 -0
  52. data/spec/data/markdowntest/Amps and angle encoding.html +17 -0
  53. data/spec/data/markdowntest/Amps and angle encoding.text +21 -0
  54. data/spec/data/markdowntest/Auto links.html +18 -0
  55. data/spec/data/markdowntest/Auto links.text +13 -0
  56. data/spec/data/markdowntest/Backslash escapes.html +118 -0
  57. data/spec/data/markdowntest/Backslash escapes.text +120 -0
  58. data/spec/data/markdowntest/Blockquotes with code blocks.html +15 -0
  59. data/spec/data/markdowntest/Blockquotes with code blocks.text +11 -0
  60. data/spec/data/markdowntest/Code Blocks.html +18 -0
  61. data/spec/data/markdowntest/Code Blocks.text +14 -0
  62. data/spec/data/markdowntest/Code Spans.html +5 -0
  63. data/spec/data/markdowntest/Code Spans.text +5 -0
  64. data/spec/data/markdowntest/Hard-wrapped paragraphs with list-like lines.html +8 -0
  65. data/spec/data/markdowntest/Hard-wrapped paragraphs with list-like lines.text +8 -0
  66. data/spec/data/markdowntest/Horizontal rules.html +71 -0
  67. data/spec/data/markdowntest/Horizontal rules.text +67 -0
  68. data/spec/data/markdowntest/Inline HTML (Advanced).html +15 -0
  69. data/spec/data/markdowntest/Inline HTML (Advanced).text +15 -0
  70. data/spec/data/markdowntest/Inline HTML (Simple).html +72 -0
  71. data/spec/data/markdowntest/Inline HTML (Simple).text +69 -0
  72. data/spec/data/markdowntest/Inline HTML comments.html +13 -0
  73. data/spec/data/markdowntest/Inline HTML comments.text +13 -0
  74. data/spec/data/markdowntest/Links, inline style.html +11 -0
  75. data/spec/data/markdowntest/Links, inline style.text +12 -0
  76. data/spec/data/markdowntest/Links, reference style.html +52 -0
  77. data/spec/data/markdowntest/Links, reference style.text +71 -0
  78. data/spec/data/markdowntest/Links, shortcut references.html +9 -0
  79. data/spec/data/markdowntest/Links, shortcut references.text +20 -0
  80. data/spec/data/markdowntest/Literal quotes in titles.html +3 -0
  81. data/spec/data/markdowntest/Literal quotes in titles.text +7 -0
  82. data/spec/data/markdowntest/Markdown Documentation - Basics.html +314 -0
  83. data/spec/data/markdowntest/Markdown Documentation - Basics.text +306 -0
  84. data/spec/data/markdowntest/Markdown Documentation - Syntax.html +942 -0
  85. data/spec/data/markdowntest/Markdown Documentation - Syntax.text +888 -0
  86. data/spec/data/markdowntest/Nested blockquotes.html +9 -0
  87. data/spec/data/markdowntest/Nested blockquotes.text +5 -0
  88. data/spec/data/markdowntest/Ordered and unordered lists.html +148 -0
  89. data/spec/data/markdowntest/Ordered and unordered lists.text +131 -0
  90. data/spec/data/markdowntest/Strong and em together.html +7 -0
  91. data/spec/data/markdowntest/Strong and em together.text +7 -0
  92. data/spec/data/markdowntest/Tabs.html +25 -0
  93. data/spec/data/markdowntest/Tabs.text +21 -0
  94. data/spec/data/markdowntest/Tidyness.html +8 -0
  95. data/spec/data/markdowntest/Tidyness.text +5 -0
  96. data/spec/data/ml-announce.txt +17 -0
  97. data/spec/data/re-overflow.txt +67 -0
  98. data/spec/data/re-overflow2.txt +281 -0
  99. data/spec/lib/constants.rb +5 -0
  100. data/spec/lib/helpers.rb +137 -0
  101. data/spec/lib/matchers.rb +235 -0
  102. data/spec/markdowntest_spec.rb +76 -0
  103. metadata +305 -0
@@ -0,0 +1,281 @@
1
+ <strong>iFotobilder</strong> will be an iPhoto export plugin that will let you manage your Fotobilder pictures through iPhoto.
2
+
3
+ ## Getting Started
4
+
5
+ Since iPhoto's APIs aren't public, and because my Objective C is extremely rusty, I wanted a couple of examples of other people's plugins before I dove into this.
6
+
7
+ Here's what I found:
8
+
9
+ * [Writing Plugins for Cocoa][1]
10
+
11
+ [1]: http://www.stone.com/The_Cocoa_Files/Writing_PlugIns.html
12
+
13
+ ## The iPhoto Export API
14
+
15
+ Using the `class-dump` tool, I dumped the export API:
16
+
17
+ /*
18
+ * Generated by class-dump 3.0.
19
+ *
20
+ * class-dump is Copyright (C) 1997-1998, 2000-2001, 2004 by Steve Nygard.
21
+ */
22
+
23
+ /*
24
+ * File: /Applications/iPhoto.app/Contents/MacOS/iPhoto
25
+ */
26
+
27
+ @protocol ExportImageProtocol
28
+ - (unsigned int)imageCount;
29
+ - (BOOL)imageIsPortraitAtIndex:(unsigned int)fp20;
30
+ - (struct _NSSize)imageSizeAtIndex:(unsigned int)fp16;
31
+ - (unsigned int)imageFormatAtIndex:(unsigned int)fp16;
32
+ - (id)imageCaptionAtIndex:(unsigned int)fp16;
33
+ - (id)imagePathAtIndex:(unsigned int)fp16;
34
+ - (id)thumbnailPathAtIndex:(unsigned int)fp16;
35
+ - (id)imageDictionaryAtIndex:(unsigned int)fp16;
36
+ - (float)imageAspectRatioAtIndex:(unsigned int)fp16;
37
+ - (id)albumName;
38
+ - (id)albumMusicPath;
39
+ - (unsigned int)albumCount;
40
+ - (unsigned int)albumPositionOfImageAtIndex:(unsigned int)fp16;
41
+ - (id)window;
42
+ - (void)enableControls;
43
+ - (void)disableControls;
44
+ - (void)clickExport;
45
+ - (void)startExport;
46
+ - (void)cancelExport;
47
+ - (void)cancelExportBeforeBeginning;
48
+ - (id)directoryPath;
49
+ - (id)temporaryDirectory;
50
+ - (BOOL)doesFileExist:(id)fp16;
51
+ - (BOOL)doesDirectoryExist:(id)fp16;
52
+ - (BOOL)createDir:(id)fp16;
53
+ - (id)uniqueSubPath:(id)fp12 child:(id)fp20;
54
+ - (id)makeUniquePath:(id)fp16;
55
+ - (id)makeUniqueFilePath:(id)fp12 extension:(id)fp20;
56
+ - (id)makeUniqueFileNameWithTime:(id)fp16;
57
+ - (BOOL)makeFSSpec:(id)fp12 spec:(struct FSSpec *)fp20;
58
+ - (id)pathForFSSpec:(id)fp16;
59
+ - (BOOL)getFSRef:(struct FSRef *)fp12 forPath:(id)fp16 isDirectory:(BOOL)fp27;
60
+ - (id)pathForFSRef:(struct FSRef *)fp16;
61
+ - (unsigned long)countFiles:(id)fp12 descend:(BOOL)fp23;
62
+ - (unsigned long)countFilesFromArray:(id)fp12 descend:(BOOL)fp23;
63
+ - (unsigned long long)sizeAtPath:(id)fp12 count:(unsigned long *)fp16 physical:(BOOL)fp27;
64
+ - (BOOL)isAliasFileAtPath:(id)fp16;
65
+ - (id)pathContentOfAliasAtPath:(id)fp16;
66
+ - (id)stringByResolvingAliasesInPath:(id)fp16;
67
+ - (BOOL)ensurePermissions:(unsigned long)fp12 forPath:(id)fp20;
68
+ - (id)validFilename:(id)fp16;
69
+ - (id)getExtensionForImageFormat:(unsigned int)fp16;
70
+ - (unsigned int)getImageFormatForExtension:(id)fp16;
71
+ - (struct OpaqueGrafPtr *)uncompressImage:(id)fp12 size:(struct _NSSize)fp16 pixelFormat:(unsigned int)fp24 rotation:(float)fp32;
72
+ - (void *)createThumbnailer;
73
+ - (void *)retainThumbnailer:(void *)fp16;
74
+ - (void *)autoreleaseThumbnailer:(void *)fp16;
75
+ - (void)releaseThumbnailer:(void *)fp16;
76
+ - (void)setThumbnailer:(void *)fp16 maxBytes:(unsigned int)fp20 maxWidth:(unsigned int)fp24 maxHeight:(unsigned int)fp32;
77
+ - (struct _NSSize)thumbnailerMaxBounds:(void *)fp16;
78
+ - (void)setThumbnailer:(void *)fp12 quality:(int)fp20;
79
+ - (int)thumbnailerQuality:(void *)fp16;
80
+ - (void)setThumbnailer:(void *)fp12 rotation:(float)fp20;
81
+ - (float)thumbnailerRotation:(void *)fp16;
82
+ - (void)setThumbnailer:(void *)fp12 outputFormat:(unsigned int)fp20;
83
+ - (unsigned int)thumbnailerOutputFormat:(void *)fp16;
84
+ - (void)setThumbnailer:(void *)fp12 outputExtension:(id)fp20;
85
+ - (id)thumbnailerOutputExtension:(void *)fp16;
86
+ - (BOOL)thumbnailer:(void *)fp16 createThumbnail:(id)fp20 dest:(id)fp28;
87
+ - (struct _NSSize)lastImageSize:(void *)fp20;
88
+ - (struct _NSSize)lastThumbnailSize:(void *)fp16;
89
+ @end
90
+
91
+ @protocol ExportPluginBoxProtocol
92
+ - (BOOL)performKeyEquivalent:(id)fp16;
93
+ @end
94
+
95
+ @protocol ExportPluginProtocol
96
+ - (id)initWithExportImageObj:(id)fp16;
97
+ - (id)settingsView;
98
+ - (id)firstView;
99
+ - (id)lastView;
100
+ - (void)viewWillBeActivated;
101
+ - (void)viewWillBeDeactivated;
102
+ - (id)requiredFileType;
103
+ - (BOOL)wantsDestinationPrompt;
104
+ - (id)getDestinationPath;
105
+ - (id)defaultFileName;
106
+ - (id)defaultDirectory;
107
+ - (BOOL)treatSingleSelectionDifferently;
108
+ - (BOOL)validateUserCreatedPath:(id)fp16;
109
+ - (void)clickExport;
110
+ - (void)startExport:(id)fp16;
111
+ - (void)performExport:(id)fp16;
112
+ - (CDAnonymousStruct12 *)progress;
113
+ - (void)lockProgress;
114
+ - (void)unlockProgress;
115
+ - (void)cancelExport;
116
+ - (id)name;
117
+ - (id)description;
118
+ @end
119
+
120
+ @interface ExportController : NSObject
121
+ {
122
+ id mWindow;
123
+ id mExportView;
124
+ id mExportButton;
125
+ id mImageCount;
126
+ ExportMgr *mExportMgr;
127
+ ExportMgrRec *mCurrentPluginRec;
128
+ ProgressController *mProgressController;
129
+ BOOL mCancelExport;
130
+ NSTimer *mTimer;
131
+ NSString *mDirectoryPath;
132
+ }
133
+
134
+ - (void)awakeFromNib;
135
+ - (void)dealloc;
136
+ - (id)currentPlugin;
137
+ - (id)currentPluginRec;
138
+ - (void)setCurrentPluginRec:(id)fp12;
139
+ - (id)directoryPath;
140
+ - (void)setDirectoryPath:(id)fp12;
141
+ - (void)show;
142
+ - (void)_openPanelDidEnd:(id)fp12 returnCode:(int)fp16 contextInfo:(void *)fp20;
143
+ - (id)panel:(id)fp12 userEnteredFilename:(id)fp16 confirmed:(BOOL)fp20;
144
+ - (BOOL)panel:(id)fp12 shouldShowFilename:(id)fp16;
145
+ - (BOOL)panel:(id)fp12 isValidFilename:(id)fp16;
146
+ - (BOOL)filesWillFitOnDisk;
147
+ - (void)export:(id)fp12;
148
+ - (void)_exportThread:(id)fp12;
149
+ - (void)_exportProgress:(id)fp12;
150
+ - (void)startExport:(id)fp12;
151
+ - (void)finishExport;
152
+ - (void)cancelExport;
153
+ - (void)cancel:(id)fp12;
154
+ - (void)enableControls;
155
+ - (id)window;
156
+ - (void)disableControls;
157
+ - (void)tabView:(id)fp12 willSelectTabViewItem:(id)fp16;
158
+ - (void)tabView:(id)fp12 didSelectTabViewItem:(id)fp16;
159
+ - (void)selectExporter:(id)fp12;
160
+ - (id)exportView;
161
+ - (BOOL)_hasPlugins;
162
+ - (void)_resizeExporterToFitView:(id)fp12;
163
+ - (void)_updateImageCount;
164
+
165
+ @end
166
+
167
+ @interface ExportMgr : NSObject <ExportImageProtocol>
168
+ {
169
+ ArchiveDocument *mDocument;
170
+ NSMutableArray *mExporters;
171
+ Album *mExportAlbum;
172
+ NSArray *mSelection;
173
+ ExportController *mExportController;
174
+ }
175
+
176
+ + (id)exportMgr;
177
+ + (id)exportMgrNoAlloc;
178
+ - (id)init;
179
+ - (void)dealloc;
180
+ - (void)releasePlugins;
181
+ - (void)setExportController:(id)fp12;
182
+ - (id)exportController;
183
+ - (void)setDocument:(id)fp12;
184
+ - (id)document;
185
+ - (void)updateDocumentSelection;
186
+ - (unsigned int)count;
187
+ - (id)recAtIndex:(unsigned int)fp12;
188
+ - (void)scanForExporters;
189
+ - (unsigned int)imageCount;
190
+ - (BOOL)imageIsPortraitAtIndex:(unsigned int)fp12;
191
+ - (id)imagePathAtIndex:(unsigned int)fp12;
192
+ - (struct _NSSize)imageSizeAtIndex:(unsigned int)fp16;
193
+ - (unsigned int)imageFormatAtIndex:(unsigned int)fp12;
194
+ - (id)imageCaptionAtIndex:(unsigned int)fp12;
195
+ - (id)thumbnailPathAtIndex:(unsigned int)fp12;
196
+ - (id)imageDictionaryAtIndex:(unsigned int)fp12;
197
+ - (float)imageAspectRatioAtIndex:(unsigned int)fp12;
198
+ - (id)albumName;
199
+ - (id)albumMusicPath;
200
+ - (unsigned int)albumCount;
201
+ - (unsigned int)albumPositionOfImageAtIndex:(unsigned int)fp12;
202
+ - (id)imageRecAtIndex:(unsigned int)fp12;
203
+ - (id)currentAlbum;
204
+ - (void)enableControls;
205
+ - (void)disableControls;
206
+ - (id)window;
207
+ - (void)clickExport;
208
+ - (void)startExport;
209
+ - (void)cancelExport;
210
+ - (void)cancelExportBeforeBeginning;
211
+ - (id)directoryPath;
212
+ - (void)_copySelection:(id)fp12;
213
+ - (id)temporaryDirectory;
214
+ - (BOOL)doesFileExist:(id)fp12;
215
+ - (BOOL)doesDirectoryExist:(id)fp12;
216
+ - (BOOL)createDir:(id)fp12;
217
+ - (id)uniqueSubPath:(id)fp12 child:(id)fp16;
218
+ - (id)makeUniquePath:(id)fp12;
219
+ - (id)makeUniqueFilePath:(id)fp12 extension:(id)fp16;
220
+ - (id)makeUniqueFileNameWithTime:(id)fp12;
221
+ - (BOOL)makeFSSpec:(id)fp12 spec:(struct FSSpec *)fp16;
222
+ - (id)pathForFSSpec:(id)fp12;
223
+ - (BOOL)getFSRef:(struct FSRef *)fp12 forPath:(id)fp16 isDirectory:(BOOL)fp20;
224
+ - (id)pathForFSRef:(struct FSRef *)fp12;
225
+ - (unsigned long)countFiles:(id)fp12 descend:(BOOL)fp16;
226
+ - (unsigned long)countFilesFromArray:(id)fp12 descend:(BOOL)fp16;
227
+ - (unsigned long long)sizeAtPath:(id)fp12 count:(unsigned long *)fp16 physical:(BOOL)fp20;
228
+ - (BOOL)isAliasFileAtPath:(id)fp12;
229
+ - (id)pathContentOfAliasAtPath:(id)fp12;
230
+ - (id)stringByResolvingAliasesInPath:(id)fp12;
231
+ - (BOOL)ensurePermissions:(unsigned long)fp12 forPath:(id)fp16;
232
+ - (id)validFilename:(id)fp12;
233
+ - (id)getExtensionForImageFormat:(unsigned int)fp12;
234
+ - (unsigned int)getImageFormatForExtension:(id)fp12;
235
+ - (struct OpaqueGrafPtr *)uncompressImage:(id)fp12 size:(struct _NSSize)fp16 pixelFormat:(unsigned int)fp24 rotation:(float)fp36;
236
+ - (void *)createThumbnailer;
237
+ - (void *)retainThumbnailer:(void *)fp12;
238
+ - (void *)autoreleaseThumbnailer:(void *)fp12;
239
+ - (void)releaseThumbnailer:(void *)fp12;
240
+ - (void)setThumbnailer:(void *)fp12 maxBytes:(unsigned int)fp16 maxWidth:(unsigned int)fp20 maxHeight:(unsigned int)fp24;
241
+ - (struct _NSSize)thumbnailerMaxBounds:(void *)fp16;
242
+ - (void)setThumbnailer:(void *)fp12 quality:(int)fp16;
243
+ - (int)thumbnailerQuality:(void *)fp12;
244
+ - (void)setThumbnailer:(void *)fp12 rotation:(float)fp36;
245
+ - (float)thumbnailerRotation:(void *)fp12;
246
+ - (void)setThumbnailer:(void *)fp12 outputFormat:(unsigned int)fp16;
247
+ - (unsigned int)thumbnailerOutputFormat:(void *)fp12;
248
+ - (void)setThumbnailer:(void *)fp12 outputExtension:(id)fp16;
249
+ - (id)thumbnailerOutputExtension:(void *)fp12;
250
+ - (BOOL)thumbnailer:(void *)fp12 createThumbnail:(id)fp16 dest:(id)fp20;
251
+ - (struct _NSSize)lastImageSize:(void *)fp16;
252
+ - (struct _NSSize)lastThumbnailSize:(void *)fp16;
253
+
254
+ @end
255
+
256
+ @interface ExportMgrRec : NSObject
257
+ {
258
+ NSString *mPath;
259
+ NSBundle *mBundle;
260
+ id mPlugin;
261
+ struct _NSSize mViewSize;
262
+ }
263
+
264
+ - (void)dealloc;
265
+ - (BOOL)isEqual:(id)fp12;
266
+ - (id)description;
267
+ - (id)initWithPath:(id)fp12;
268
+ - (id)path;
269
+ - (id)bundle;
270
+ - (id)bundleInfo;
271
+ - (BOOL)isValidExportPlugin;
272
+ - (BOOL)loadPlugin;
273
+ - (id)exportPlugin;
274
+ - (void)unloadPlugin;
275
+ - (id)view;
276
+ - (struct _NSSize)viewSize;
277
+ - (void)setPath:(id)fp12;
278
+ - (void)setBundle:(id)fp12;
279
+
280
+ @end
281
+
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/ruby
2
+
3
+ module BlueCloth::TestConstants
4
+
5
+ end
@@ -0,0 +1,137 @@
1
+ #!/usr/bin/ruby
2
+ # encoding: utf-8
3
+
4
+ require 'spec'
5
+ require 'bluecloth'
6
+
7
+
8
+ module BlueCloth::SpecHelpers
9
+
10
+ ###############
11
+ module_function
12
+ ###############
13
+
14
+ end
15
+
16
+
17
+ # Override the badly-formatted output of the RSpec HTML formatter
18
+ require 'spec/runner/formatter/html_formatter'
19
+
20
+ class Spec::Runner::Formatter::HtmlFormatter
21
+ def example_failed( example, counter, failure )
22
+ failure_style = failure.pending_fixed? ? 'pending_fixed' : 'failed'
23
+
24
+ unless @header_red
25
+ @output.puts " <script type=\"text/javascript\">makeRed('rspec-header');</script>"
26
+ @header_red = true
27
+ end
28
+
29
+ unless @example_group_red
30
+ css_class = 'example_group_%d' % [current_example_group_number||0]
31
+ @output.puts " <script type=\"text/javascript\">makeRed('#{css_class}');</script>"
32
+ @example_group_red = true
33
+ end
34
+
35
+ move_progress()
36
+
37
+ @output.puts " <dd class=\"spec #{failure_style}\">",
38
+ " <span class=\"failed_spec_name\">#{h(example.description)}</span>",
39
+ " <div class=\"failure\" id=\"failure_#{counter}\">"
40
+ if failure.exception
41
+ backtrace = format_backtrace( failure.exception.backtrace )
42
+ message = failure.exception.message
43
+
44
+ @output.puts " <div class=\"message\"><code>#{h message}</code></div>",
45
+ " <div class=\"backtrace\"><pre>#{backtrace}</pre></div>"
46
+ end
47
+
48
+ if extra = extra_failure_content( failure )
49
+ @output.puts( extra )
50
+ end
51
+
52
+ @output.puts " </div>",
53
+ " </dd>"
54
+ @output.flush
55
+ end
56
+
57
+
58
+ alias_method :default_global_styles, :global_styles
59
+
60
+ def global_styles
61
+ css = default_global_styles()
62
+ css << %Q{
63
+ /* Stuff added by #{__FILE__} */
64
+
65
+ /* Overrides */
66
+ #rspec-header {
67
+ -webkit-box-shadow: #333 0 2px 5px;
68
+ margin-bottom: 1em;
69
+ }
70
+
71
+ .example_group dt {
72
+ -webkit-box-shadow: #333 0 2px 3px;
73
+ }
74
+
75
+ /* Style for log output */
76
+ dd.log-message {
77
+ background: #eee;
78
+ padding: 0 2em;
79
+ margin: 0.2em 1em;
80
+ border-bottom: 1px dotted #999;
81
+ border-top: 1px dotted #999;
82
+ text-indent: -1em;
83
+ }
84
+
85
+ /* Parts of the message */
86
+ dd.log-message .log-time {
87
+ font-weight: bold;
88
+ }
89
+ dd.log-message .log-time:after {
90
+ content: ": ";
91
+ }
92
+ dd.log-message .log-level {
93
+ font-variant: small-caps;
94
+ border: 1px solid #ccc;
95
+ padding: 1px 2px;
96
+ }
97
+ dd.log-message .log-name {
98
+ font-size: 1.2em;
99
+ color: #1e51b2;
100
+ }
101
+ dd.log-message .log-name:before { content: "«"; }
102
+ dd.log-message .log-name:after { content: "»"; }
103
+
104
+ dd.log-message .log-message-text {
105
+ padding-left: 4px;
106
+ font-family: Monaco, "Andale Mono", "Vera Sans Mono", mono;
107
+ }
108
+
109
+
110
+ /* Distinguish levels */
111
+ dd.log-message.debug { color: #666; }
112
+ dd.log-message.info {}
113
+
114
+ dd.log-message.warn,
115
+ dd.log-message.error {
116
+ background: #ff9;
117
+ }
118
+ dd.log-message.error .log-level,
119
+ dd.log-message.error .log-message-text {
120
+ color: #900;
121
+ }
122
+ dd.log-message.fatal {
123
+ background: #900;
124
+ color: white;
125
+ font-weight: bold;
126
+ border: 0;
127
+ }
128
+ dd.log-message.fatal .log-name {
129
+ color: white;
130
+ }
131
+ }
132
+
133
+ return css
134
+ end
135
+ end
136
+
137
+
@@ -0,0 +1,235 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'bluecloth'
4
+ require 'diff/lcs'
5
+ require 'diff/lcs/callbacks'
6
+ require 'spec/lib/constants'
7
+ require 'rbconfig'
8
+
9
+
10
+ ### Fixturing functions
11
+ module BlueCloth::Matchers
12
+
13
+ ### Matcher for comparing output of a BlueCloth-generated HTML fragment against a known-good
14
+ ### string.
15
+ class TransformMatcher
16
+
17
+ ### Create a new matcher for the given +html+
18
+ def initialize( html )
19
+ @html = html
20
+ end
21
+
22
+ ### Strip tab indentation from the expected HTML output.
23
+ def without_indentation
24
+ if indent = @html[/\A\t+/]
25
+ indent.gsub!( /\A\n/m, '' )
26
+ @html.gsub!( /^#{indent}/m, '' )
27
+ end
28
+
29
+ return self
30
+ end
31
+
32
+
33
+ ### Returns true if the HTML generated by the given +bluecloth+ object matches the
34
+ ### expected HTML, comparing only the salient document structures.
35
+ def matches?( bluecloth )
36
+ @bluecloth = bluecloth
37
+ @output_html = bluecloth.to_html.gsub( /\n\n\n/, "\n\n" )
38
+ return @output_html.strip == @html.strip
39
+ end
40
+
41
+ ### Build a failure message for the matching case.
42
+ def failure_message
43
+ if self.should_output_html?
44
+ patch = self.make_html_patch( @html, @output_html )
45
+ return %{
46
+ <p>Expected the generated html:<br />
47
+ <pre><code>#@output_html</code></pre>
48
+ to be the same as:<br />
49
+ <pre><code>#@html</code></pre>
50
+ </p>
51
+ <p>Diffs:</p>
52
+ #{patch}
53
+ }
54
+ else
55
+ patch = self.make_patch( @html, @output_html )
56
+ return ("Expected the generated html:\n\n %p\n\nto be the same as:\n\n" +
57
+ " %p\n\nDiffs:\n\n%s") % [ @output_html, @html, patch ]
58
+ end
59
+ end
60
+
61
+ ### Build a failure message for the non-matching case.
62
+ def negative_failure_message
63
+ return "Expected the generated html:\n\n %p\n\nnot to be the same as:\n\n %p\n\n" %
64
+ [ @output_html, @html ]
65
+ end
66
+
67
+
68
+ ### Returns true if it appears HTML output should be used instead of plain-text. This
69
+ ### will be true if running from TextMate or if the HTML_LOGGING environment variable
70
+ ### is set.
71
+ def should_output_html?
72
+ return false
73
+ # return ENV['HTML_LOGGING'] ||
74
+ # (ENV['TM_FILENAME'] && ENV['TM_FILENAME'] =~ /_spec\.rb/)
75
+ end
76
+
77
+
78
+ ### Compute a patch between the given +expected+ output and the +actual+ output
79
+ ### and return it as a string.
80
+ def make_patch( expected, actual )
81
+ diffs = Diff::LCS.sdiff( expected.split("\n"), actual.split("\n"),
82
+ Diff::LCS::ContextDiffCallbacks )
83
+
84
+ maxcol = diffs.flatten.
85
+ collect {|d| [d.old_element.to_s.length, d.new_element.to_s.length ] }.
86
+ flatten.max || 0
87
+ maxcol += 4
88
+
89
+ patch = " %#{maxcol}s | %s\n" % [ "Expected", "Actual" ]
90
+ patch << diffs.collect do |changeset|
91
+ changeset.collect do |change|
92
+ "%s [%03d, %03d]: %#{maxcol}s | %-#{maxcol}s" % [
93
+ change.action,
94
+ change.old_position,
95
+ change.new_position,
96
+ change.old_element.inspect,
97
+ change.new_element.inspect,
98
+ ]
99
+ end.join("\n")
100
+ end.join("\n---\n")
101
+ end
102
+
103
+ ### Compute a patch similar to #make_patch, but output HTML instead of plain text.
104
+ def make_html_patch( expected, actual )
105
+ diffs = Diff::LCS.sdiff( expected.split("\n"), actual.split("\n"),
106
+ Diff::LCS::ContextDiffCallbacks )
107
+
108
+ patch = %{
109
+ <table>
110
+ <caption>Diffs</caption>
111
+ <thead>
112
+ <tr><th>Op</th><th>Pos</th><th>Expected</th><th>Actual</th></tr>
113
+ </thead>
114
+ <tbody>
115
+ }
116
+ patch << diffs.collect do |changeset|
117
+ changeset.collect do |change|
118
+ "<tr><td>%s</td><td>[%03d, %03d]</td><td>%s</td><td>%s</td></tr>" % [
119
+ change.action,
120
+ change.old_position,
121
+ change.new_position,
122
+ change.old_element.inspect,
123
+ change.new_element.inspect,
124
+ ]
125
+ end.join("\n")
126
+ end.join( "</tbody><tbody>" )
127
+ patch << %{</tbody></table>\n}
128
+ end
129
+
130
+ end
131
+
132
+ ### Variant of the regular TransformMatcher that normalizes the two strings using the 'tidy'
133
+ ### library before comparing.
134
+ class TidyTransformMatcher < TransformMatcher
135
+
136
+ TIDY_OPTIONS = {}
137
+ @tidy = nil
138
+
139
+ ### Fetch the class-global Tidy object, creating it if necessary
140
+ def self::tidy_object
141
+ unless @tidy
142
+ require 'tidy'
143
+ soext = Config::CONFIG['LIBRUBY_ALIASES'].sub( /.*\./, '' )
144
+ Tidy.path = "libtidy.#{soext}"
145
+ @tidy = Tidy.new( TIDY_OPTIONS )
146
+ end
147
+
148
+ return @tidy
149
+ end
150
+
151
+
152
+ ### Set the matcher's expected output to a tidied version of the input +html+.
153
+ def initialize( html )
154
+ @html = self.class.tidy_object.clean( html )
155
+ end
156
+
157
+
158
+ ### Returns true if the HTML generated by the given +bluecloth+ object matches the
159
+ ### expected HTML after normalizing them both with 'tidy'.
160
+ def matches?( bluecloth )
161
+ @bluecloth = bluecloth
162
+ @output_html = self.class.tidy_object.clean( bluecloth.to_html )
163
+ return @output_html == @html
164
+ end
165
+
166
+ end
167
+
168
+
169
+ class TransformRegexpMatcher
170
+
171
+ ### Create a new matcher for the given +regexp+
172
+ def initialize( regexp )
173
+ @regexp = regexp
174
+ end
175
+
176
+ ### Returns true if the regexp associated with this matcher matches the output generated
177
+ ### by the specified +bluecloth+ object.
178
+ def matches?( bluecloth )
179
+ @bluecloth = bluecloth
180
+ @output_html = bluecloth.to_html
181
+ return @output_html =~ @regexp
182
+ end
183
+
184
+ ### Build a failure message for the matching case.
185
+ def failure_message
186
+ return "Expected the generated html:\n\n %pto match the regexp:\n\n%p\n\n" %
187
+ [ @output_html, @regexp ]
188
+ end
189
+
190
+
191
+ ### Build a failure message for the negative matching case.
192
+ def negative_failure_message
193
+ return "Expected the generated html:\n\n %pnot to match the regexp:\n\n%p\n\n" %
194
+ [ @output_html, @regexp ]
195
+ end
196
+ end
197
+
198
+
199
+ ### Create a new BlueCloth object out of the given +string+ and +options+ and
200
+ ### return it.
201
+ def the_markdown( string, *options )
202
+ return BlueCloth.new( string, *options )
203
+ end
204
+
205
+
206
+ ### Strip indentation from the given +string+, create a new BlueCloth object
207
+ ### out of the result and any +options+, and return it.
208
+ def the_indented_markdown( string, *options )
209
+ if indent = string[/\A\t+/]
210
+ indent.gsub!( /\A\n/m, '' )
211
+ $stderr.puts "Source indent is: %p" % [ indent ] if $DEBUG
212
+ string.gsub!( /^#{indent}/m, '' )
213
+ end
214
+
215
+ return BlueCloth.new( string, *options )
216
+ end
217
+
218
+
219
+ ### Generate a matcher that expects to equal the given +html+.
220
+ def be_transformed_into( html )
221
+ return BlueCloth::Matchers::TransformMatcher.new( html )
222
+ end
223
+
224
+ ### Generate a matcher that expects to match a normalized version of the specified +html+.
225
+ def be_transformed_into_normalized_html( html )
226
+ return BlueCloth::Matchers::TidyTransformMatcher.new( html )
227
+ end
228
+
229
+ ### Generate a matcher that expects to match the given +regexp+.
230
+ def be_transformed_into_html_matching( regexp )
231
+ return BlueCloth::Matchers::TransformMatcher.new( regexp )
232
+ end
233
+
234
+ end # module BlueCloth::Matchers
235
+