webby 0.8.4 → 0.9.0

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.
Files changed (165) hide show
  1. data/History.txt +19 -0
  2. data/Manifest.txt +104 -91
  3. data/README.txt +27 -45
  4. data/Rakefile +4 -5
  5. data/bin/webby +2 -1
  6. data/bin/webby-gen +8 -0
  7. data/{data/Rakefile → examples/blog/Sitefile} +0 -2
  8. data/examples/{webby → blog}/tasks/blog.rake +5 -4
  9. data/{data → examples/blog}/templates/atom_feed.erb +10 -11
  10. data/{data → examples/blog}/templates/blog/month.erb +1 -1
  11. data/{data → examples/blog}/templates/blog/post.erb +1 -0
  12. data/{data → examples/blog}/templates/blog/year.erb +1 -1
  13. data/examples/presentation/{Rakefile → Sitefile} +0 -4
  14. data/examples/presentation/content/{_sample_code.txt → presentation/_sample_code.txt} +0 -0
  15. data/examples/presentation/content/{index.txt → presentation/index.txt} +0 -0
  16. data/{data/content → examples/presentation/content/presentation}/s5/blank.gif +0 -0
  17. data/{data/content → examples/presentation/content/presentation}/s5/bodybg.gif +0 -0
  18. data/{data/content → examples/presentation/content/presentation}/s5/framing.css +0 -0
  19. data/{data/content → examples/presentation/content/presentation}/s5/iepngfix.htc +0 -0
  20. data/{data/content → examples/presentation/content/presentation}/s5/opera.css +0 -0
  21. data/{data/content → examples/presentation/content/presentation}/s5/outline.css +0 -0
  22. data/{data/content → examples/presentation/content/presentation}/s5/pretty.css +0 -0
  23. data/{data/content → examples/presentation/content/presentation}/s5/print.css +0 -0
  24. data/{data/content → examples/presentation/content/presentation}/s5/s5-core.css +0 -0
  25. data/{data/content → examples/presentation/content/presentation}/s5/slides.css +0 -0
  26. data/{data/content → examples/presentation/content/presentation}/s5/slides.js +0 -0
  27. data/examples/presentation/layouts/{presentation.rhtml → presentation.txt} +3 -3
  28. data/examples/presentation/templates/{_partial.erb → _code_partial.erb} +0 -0
  29. data/examples/tumblog/Sitefile +9 -0
  30. data/examples/tumblog/content/css/tumblog.css +308 -0
  31. data/examples/tumblog/content/images/tumblog/permalink.gif +0 -0
  32. data/examples/tumblog/content/images/tumblog/rss.gif +0 -0
  33. data/examples/tumblog/content/tumblog/200806/the-noble-chicken/index.txt +12 -0
  34. data/examples/tumblog/content/tumblog/200807/historical-perspectives-on-the-classic-chicken-joke/index.txt +12 -0
  35. data/examples/tumblog/content/tumblog/200807/mad-city-chickens/index.txt +10 -0
  36. data/examples/tumblog/content/tumblog/200807/the-wisdom-of-the-dutch/index.txt +11 -0
  37. data/examples/tumblog/content/tumblog/200807/up-a-tree/index.txt +13 -0
  38. data/examples/tumblog/content/tumblog/index.txt +37 -0
  39. data/examples/tumblog/content/tumblog/rss.txt +37 -0
  40. data/examples/tumblog/layouts/tumblog/default.txt +44 -0
  41. data/examples/tumblog/layouts/tumblog/post.txt +15 -0
  42. data/examples/tumblog/lib/tumblog_helper.rb +32 -0
  43. data/examples/tumblog/tasks/tumblog.rake +30 -0
  44. data/examples/tumblog/templates/atom_feed.erb +40 -0
  45. data/examples/tumblog/templates/tumblog/conversation.erb +12 -0
  46. data/examples/tumblog/templates/tumblog/link.erb +10 -0
  47. data/examples/tumblog/templates/tumblog/photo.erb +13 -0
  48. data/examples/tumblog/templates/tumblog/post.erb +12 -0
  49. data/examples/tumblog/templates/tumblog/quote.erb +11 -0
  50. data/examples/webby/{Rakefile → Sitefile} +2 -2
  51. data/examples/webby/content/communicate/index.txt +28 -0
  52. data/examples/webby/content/css/background.gif +0 -0
  53. data/examples/webby/content/css/site.css +52 -20
  54. data/examples/webby/content/index.txt +7 -8
  55. data/examples/webby/content/learn/index.txt +28 -0
  56. data/examples/webby/content/reference/index.txt +3 -1
  57. data/examples/webby/content/release-notes/rel-0-9-0/index.txt +73 -0
  58. data/examples/webby/content/sitemap.txt +31 -0
  59. data/examples/webby/content/tips_and_tricks/index.txt +1 -1
  60. data/examples/webby/content/tutorial/index.txt +17 -17
  61. data/examples/webby/content/{manual → user-manual}/index.txt +53 -50
  62. data/examples/webby/layouts/{default.rhtml → default.txt} +8 -15
  63. data/examples/webby/templates/page.erb +1 -1
  64. data/examples/website/Sitefile +7 -0
  65. data/{data → examples/website}/content/css/blueprint/License.txt +0 -0
  66. data/{data → examples/website}/content/css/blueprint/Readme.txt +0 -0
  67. data/{data → examples/website}/content/css/blueprint/compressed/print.css +0 -0
  68. data/{data → examples/website}/content/css/blueprint/compressed/screen.css +0 -0
  69. data/{data → examples/website}/content/css/blueprint/lib/forms.css +0 -0
  70. data/{data → examples/website}/content/css/blueprint/lib/grid.css +0 -0
  71. data/{data → examples/website}/content/css/blueprint/lib/grid.png +0 -0
  72. data/{data → examples/website}/content/css/blueprint/lib/ie.css +0 -0
  73. data/{data → examples/website}/content/css/blueprint/lib/reset.css +0 -0
  74. data/{data → examples/website}/content/css/blueprint/lib/typography.css +0 -0
  75. data/{data → examples/website}/content/css/blueprint/plugins/buttons/Readme +0 -0
  76. data/{data → examples/website}/content/css/blueprint/plugins/buttons/buttons.css +0 -0
  77. data/{data → examples/website}/content/css/blueprint/plugins/buttons/icons/cross.png +0 -0
  78. data/{data → examples/website}/content/css/blueprint/plugins/buttons/icons/key.png +0 -0
  79. data/{data → examples/website}/content/css/blueprint/plugins/buttons/icons/tick.png +0 -0
  80. data/{data → examples/website}/content/css/blueprint/plugins/css-classes/Readme +0 -0
  81. data/{data → examples/website}/content/css/blueprint/plugins/css-classes/css-classes.css +0 -0
  82. data/{data → examples/website}/content/css/blueprint/plugins/fancy-type/Readme +0 -0
  83. data/{data → examples/website}/content/css/blueprint/plugins/fancy-type/fancy-type-compressed.css +0 -0
  84. data/{data → examples/website}/content/css/blueprint/plugins/fancy-type/fancy-type.css +0 -0
  85. data/{data → examples/website}/content/css/blueprint/print.css +0 -0
  86. data/{data → examples/website}/content/css/blueprint/screen.css +0 -0
  87. data/{data → examples/website}/content/css/coderay.css +0 -0
  88. data/{data → examples/website}/content/css/site.css +0 -0
  89. data/{data → examples/website}/content/index.txt +2 -1
  90. data/{data/layouts/default.rhtml → examples/website/layouts/default.txt} +1 -1
  91. data/{data → examples/website}/lib/breadcrumbs.rb +0 -0
  92. data/{data → examples/website}/templates/_partial.erb +0 -0
  93. data/{data → examples/website}/templates/page.erb +0 -0
  94. data/{data → examples/website}/templates/presentation.erb +0 -0
  95. data/lib/webby/apps/generator.rb +283 -0
  96. data/lib/webby/apps/main.rb +221 -0
  97. data/lib/webby/apps.rb +12 -0
  98. data/lib/webby/builder.rb +8 -10
  99. data/lib/webby/core_ext/enumerable.rb +11 -0
  100. data/lib/webby/{utils.rb → core_ext/hash.rb} +2 -26
  101. data/lib/webby/core_ext/kernel.rb +21 -0
  102. data/lib/webby/core_ext/string.rb +163 -0
  103. data/lib/webby/core_ext/time.rb +9 -0
  104. data/lib/webby/filters/haml.rb +13 -5
  105. data/lib/webby/filters/markdown.rb +12 -4
  106. data/lib/webby/filters/outline.rb +3 -2
  107. data/lib/webby/filters/sass.rb +12 -4
  108. data/lib/webby/filters/textile.rb +12 -4
  109. data/lib/webby/filters/tidy.rb +10 -8
  110. data/lib/webby/filters.rb +2 -0
  111. data/lib/webby/helpers/graphviz_helper.rb +1 -2
  112. data/lib/webby/helpers/tex_img_helper.rb +6 -10
  113. data/lib/webby/renderer.rb +5 -3
  114. data/lib/webby/resources/layout.rb +1 -1
  115. data/lib/webby/resources/page.rb +1 -1
  116. data/lib/webby/resources/resource.rb +13 -3
  117. data/lib/webby/resources.rb +16 -0
  118. data/{data → lib/webby}/tasks/build.rake +0 -0
  119. data/{examples/presentation → lib/webby}/tasks/create.rake +1 -1
  120. data/{data → lib/webby}/tasks/deploy.rake +0 -0
  121. data/{examples → lib}/webby/tasks/growl.rake +4 -1
  122. data/{data → lib/webby}/tasks/heel.rake +0 -0
  123. data/{data → lib/webby}/tasks/validate.rake +0 -0
  124. data/lib/webby.rb +11 -5
  125. data/spec/core_ext/hash_spec.rb +47 -0
  126. data/spec/core_ext/string_spec.rb +110 -0
  127. data/spec/core_ext/time_spec.rb +19 -0
  128. data/spec/webby/apps/generator_spec.rb +111 -0
  129. data/spec/webby/apps/main_spec.rb +75 -0
  130. data/spec/webby/helpers/capture_helper_spec.rb +1 -1
  131. data/spec/webby/resources_spec.rb +17 -0
  132. data/tasks/gem.rake +10 -2
  133. data/tasks/git.rake +41 -0
  134. data/tasks/rubyforge.rake +2 -2
  135. data/tasks/website.rake +38 -38
  136. metadata +125 -98
  137. data/data/tasks/blog.rake +0 -71
  138. data/data/tasks/create.rake +0 -22
  139. data/data/tasks/growl.rake +0 -12
  140. data/data/tasks/setup.rb +0 -17
  141. data/examples/presentation/content/s5/blank.gif +0 -0
  142. data/examples/presentation/content/s5/bodybg.gif +0 -0
  143. data/examples/presentation/content/s5/framing.css +0 -23
  144. data/examples/presentation/content/s5/iepngfix.htc +0 -42
  145. data/examples/presentation/content/s5/opera.css +0 -7
  146. data/examples/presentation/content/s5/outline.css +0 -15
  147. data/examples/presentation/content/s5/pretty.css +0 -86
  148. data/examples/presentation/content/s5/print.css +0 -1
  149. data/examples/presentation/content/s5/s5-core.css +0 -9
  150. data/examples/presentation/content/s5/slides.css +0 -3
  151. data/examples/presentation/content/s5/slides.js +0 -553
  152. data/examples/presentation/tasks/blog.rake +0 -71
  153. data/examples/presentation/tasks/build.rake +0 -27
  154. data/examples/presentation/tasks/deploy.rake +0 -22
  155. data/examples/presentation/tasks/growl.rake +0 -12
  156. data/examples/presentation/tasks/heel.rake +0 -28
  157. data/examples/presentation/tasks/setup.rb +0 -17
  158. data/examples/presentation/tasks/validate.rake +0 -19
  159. data/examples/webby/tasks/build.rake +0 -27
  160. data/examples/webby/tasks/create.rake +0 -22
  161. data/examples/webby/tasks/deploy.rake +0 -22
  162. data/examples/webby/tasks/heel.rake +0 -28
  163. data/examples/webby/tasks/setup.rb +0 -17
  164. data/examples/webby/tasks/validate.rake +0 -19
  165. data/lib/webby/main.rb +0 -227
@@ -0,0 +1,221 @@
1
+ require 'optparse'
2
+ require 'rake'
3
+
4
+ module Webby::Apps
5
+
6
+ class Main
7
+
8
+ # Create a new instance of Main, and run the +webby+ application given the
9
+ # command line _args_.
10
+ #
11
+ def self.run( args )
12
+ self.new.run args
13
+ end
14
+
15
+ # Create a new Main webby object for building websites.
16
+ #
17
+ def initialize
18
+ @stdout = $stdout
19
+ end
20
+
21
+ # Runs the main webby application. The command line arguments are passed
22
+ # in to this method as an array of strings. The command line arguments are
23
+ # parsed to figure out which rake task to invoke.
24
+ #
25
+ def run( args )
26
+ args = args.dup
27
+
28
+ parse args
29
+ init args
30
+ rake
31
+ end
32
+
33
+ # Parse the command line _args_ for options and commands to invoke.
34
+ #
35
+ def parse( args )
36
+ opts = OptionParser.new
37
+ opts.banner = 'Usage: webby [options] target [target args]'
38
+
39
+ opts.separator ''
40
+ opts.on('-D', '--describe [PATTERN]', 'describe the tasks (matching optional PATTERN), then exit') {|pattern| app.do_option('--describe', pattern)}
41
+ opts.on('-P', '--prereqs', 'display the tasks and dependencies, then exit') {app.do_option('--prereqs', nil)}
42
+ opts.on('-T', '--tasks [PATTERN]', 'display the tasks (matching optional PATTERN) with descriptions, then exit') {|pattern| app.do_option('--tasks', pattern)}
43
+ opts.on('-t', '--trace', 'turn on invoke/execute tracing, enable full backtrace') {app.do_option('--trace', nil)}
44
+
45
+ opts.separator ''
46
+ opts.separator 'common options:'
47
+
48
+ opts.on_tail( '-h', '--help', 'show this message' ) do
49
+ @stdout.puts opts
50
+ exit
51
+ end
52
+ opts.on_tail( '--version', 'show version' ) do
53
+ @stdout.puts "Webby #{::Webby::VERSION}"
54
+ exit
55
+ end
56
+
57
+ opts.parse! args
58
+
59
+ ARGV.replace Array(args.shift)
60
+ args.delete_if do |arg|
61
+ if %r/^[A-Z_]+=/ =~ arg
62
+ ARGV << arg
63
+ next true
64
+ end
65
+ false
66
+ end
67
+
68
+ args
69
+ end
70
+
71
+ # Initialize the Rake application object and load the core rake tasks, the
72
+ # site specific rake tasks, and the site specific ruby code. Any extra
73
+ # command line arguments are converted into a page name and directory that
74
+ # might get created (depending upon the task invoked).
75
+ #
76
+ def init( args )
77
+ # Make sure we're in a folder with a Sitefile
78
+ app.do_option('--rakefile', 'Sitefile')
79
+ app.do_option('--nosearch', nil)
80
+ app.do_option('--silent', nil)
81
+
82
+ unless app.have_rakefile
83
+ raise RuntimeError, "Sitefile not found"
84
+ end
85
+
86
+ import_default_tasks
87
+ import_website_tasks
88
+ require_lib_files
89
+ capture_command_line_args(args)
90
+ args
91
+ end
92
+
93
+ # Execute the rake command.
94
+ #
95
+ def rake
96
+ app.init 'webby'
97
+ app.load_rakefile
98
+ app.top_level
99
+ end
100
+
101
+ # Return the Rake application object.
102
+ #
103
+ def app
104
+ Rake.application
105
+ end
106
+
107
+ # Search for the "Sitefile" starting in the current directory and working
108
+ # upwards through the filesystem until the root of the filesystem is
109
+ # reached. If a "Sitefile" is not found, a RuntimeError is raised.
110
+ #
111
+ def find_sitefile
112
+ here = Dir.pwd
113
+ while ! app.have_rakefile
114
+ Dir.chdir("..")
115
+ if Dir.pwd == here || options.nosearch
116
+ fail "No Sitefile found"
117
+ end
118
+ here = Dir.pwd
119
+ end
120
+ end
121
+
122
+ def import_default_tasks
123
+ Dir.glob(::Webby.libpath(%w[webby tasks *.rake])).sort.each {|fn| import fn}
124
+ end
125
+
126
+ def import_website_tasks
127
+ Dir.glob(::File.join(%w[tasks *.rake])).sort.each {|fn| import fn}
128
+ end
129
+
130
+ def require_lib_files
131
+ Dir.glob(::File.join(%w[lib ** *.rb])).sort.each {|fn| require fn}
132
+ end
133
+
134
+ def capture_command_line_args(args)
135
+ args = OpenStruct.new(:raw => args)
136
+
137
+ if args.raw.size > 1
138
+ ::Webby.deprecated "multiple arguments used for page title",
139
+ "please quote the page title"
140
+ end
141
+
142
+ dashed = args.raw.join('-').downcase
143
+ spaced = args.raw.join(' ')
144
+ dir = ::File.dirname(dashed)
145
+
146
+ args.dir = ('.' == dir ? '' : dir)
147
+ args.slug = ::Webby::Resources::File.basename(dashed).to_url
148
+ args.title = ::Webby::Resources::File.basename(spaced).titlecase
149
+
150
+ # page should be dir/slug without leading /
151
+ args.page = ::File.join(args.dir, args.slug).gsub(/^\//, '')
152
+
153
+ ::Webby.site.args = args
154
+ Object.const_set(:SITE, Webby.site)
155
+ args
156
+ end
157
+
158
+ end # class Main
159
+ end # module Webby::Apps
160
+
161
+ # :stopdoc:
162
+ # Monkey patches so that rake displays the correct application name in the
163
+ # help messages.
164
+ #
165
+ class Rake::Application
166
+ def display_prerequisites
167
+ tasks.each do |t|
168
+ puts "#{name} #{t.name}"
169
+ t.prerequisites.each { |pre| puts " #{pre}" }
170
+ end
171
+ end
172
+
173
+ def display_tasks_and_comments
174
+ displayable_tasks = tasks.select { |t|
175
+ t.comment && t.name =~ options.show_task_pattern
176
+ }
177
+ if options.full_description
178
+ displayable_tasks.each do |t|
179
+ puts "#{name} #{t.name_with_args}"
180
+ t.full_comment.split("\n").each do |line|
181
+ puts " #{line}"
182
+ end
183
+ puts
184
+ end
185
+ else
186
+ width = displayable_tasks.collect { |t| t.name_with_args.length }.max || 10
187
+ max_column = 80 - name.size - width - 7
188
+ displayable_tasks.each do |t|
189
+ printf "#{name} %-#{width}s # %s\n",
190
+ t.name_with_args, truncate(t.comment, max_column)
191
+ end
192
+ end
193
+ end
194
+
195
+ # Provide standard execption handling for the given block.
196
+ def standard_exception_handling
197
+ begin
198
+ yield
199
+ rescue SystemExit => ex
200
+ # Exit silently with current status
201
+ exit(ex.status)
202
+ rescue SystemExit, GetoptLong::InvalidOption => ex
203
+ # Exit silently
204
+ exit(1)
205
+ rescue Exception => ex
206
+ # Exit with error message
207
+ $stderr.puts "webby aborted!"
208
+ $stderr.puts ex.message
209
+ if options.trace
210
+ $stderr.puts ex.backtrace.join("\n")
211
+ else
212
+ $stderr.puts ex.backtrace.find {|str| str =~ /#{@rakefile}/ } || ""
213
+ $stderr.puts "(See full trace by running task with --trace)"
214
+ end
215
+ exit(1)
216
+ end
217
+ end
218
+ end # class Rake::Application
219
+ # :startdoc:
220
+
221
+ # EOF
data/lib/webby/apps.rb ADDED
@@ -0,0 +1,12 @@
1
+
2
+ module Webby::Apps
3
+
4
+ class << self
5
+ def author() 'Tim Pease'; end
6
+ end
7
+
8
+ end # module Webby::Apps
9
+
10
+ Webby.require_all_libs_relative_to(__FILE__)
11
+
12
+ # EOF
data/lib/webby/builder.rb CHANGED
@@ -69,19 +69,17 @@ class Builder
69
69
  end
70
70
 
71
71
  # call-seq:
72
- # Builder.new_page_info( task ) => [page, title, directory]
72
+ # Builder.new_page_info => [page, title, directory]
73
73
  #
74
- def new_page_info( task )
75
- return @new_page_info if defined? @new_page_info
74
+ def new_page_info
75
+ args = Webby.site.args
76
76
 
77
- raise "Usage: rake #{ARGV.first} path" unless ARGV.length > 1
78
- page = task.application.top_level_tasks.slice!(1..-1).join('-')
79
- title = ::Webby::Resources::File.basename(page).
80
- split('-').map {|w| w.capitalize}.join(' ')
81
- dir = ::File.dirname(page)
82
- dir = '' if dir == '.'
77
+ if args.raw.empty?
78
+ task_name = Rake.application.top_level_tasks.first
79
+ raise "Usage: webby #{task_name} path"
80
+ end
83
81
 
84
- @new_page_info = [page, title, dir]
82
+ [args.page, args.title, args.dir]
85
83
  end
86
84
 
87
85
 
@@ -0,0 +1,11 @@
1
+
2
+ module Enumerable
3
+
4
+ def injecting( initial )
5
+ inject(initial) do |memo, obj|
6
+ yield(memo, obj); memo
7
+ end
8
+ end
9
+ end # module Enumerable
10
+
11
+ # EOF
@@ -1,13 +1,6 @@
1
- # :stopdoc:
2
- module Enumerable
3
- def injecting( initial )
4
- inject(initial) do |memo, obj|
5
- yield(memo, obj); memo
6
- end
7
- end
8
- end
9
1
 
10
2
  class Hash
3
+
11
4
  def sanitize!
12
5
  h = self.injecting({}) do |h, (k, v)|
13
6
  h[k] = case v
@@ -30,23 +23,6 @@ class Hash
30
23
  self.each {|k,v| h[k.to_sym] = v}
31
24
  return h
32
25
  end
33
- end
34
-
35
- class String
36
- def underscore
37
- self.
38
- gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2').
39
- gsub(/([a-z\d])([A-Z])/,'\1_\2').
40
- tr(" -", "__").
41
- downcase
42
- end
43
- end
44
-
45
- class Time
46
- def to_y
47
- self.to_yaml.slice(4..-1).strip
48
- end
49
- end
50
- # :startdoc:
26
+ end # class Hash
51
27
 
52
28
  # EOF
@@ -0,0 +1,21 @@
1
+
2
+ module Kernel
3
+
4
+ # :stopdoc:
5
+ WINDOWS = %r/djgpp|(cyg|ms|bcc)win|mingw/ =~ RUBY_PLATFORM
6
+ DEV_NULL = WINDOWS ? 'NUL:' : '/dev/null'
7
+ # :startdoc:
8
+
9
+ def cmd_available?( *args )
10
+ io = [STDOUT.dup, STDERR.dup]
11
+ STDOUT.reopen DEV_NULL
12
+ STDERR.reopen DEV_NULL
13
+ system(*(args.flatten))
14
+ ensure
15
+ STDOUT.reopen io.first
16
+ STDERR.reopen io.last
17
+ $stdout, $stderr = STDOUT, STDERR
18
+ end
19
+ end # module Kernel
20
+
21
+ # EOF
@@ -0,0 +1,163 @@
1
+
2
+ class String
3
+
4
+ def self.small_words
5
+ @small_words ||= %w(a an and as at but by en for if in of on or the to v[.]? via vs[.]?)
6
+ end
7
+
8
+ def /( path )
9
+ ::File.join(self, path)
10
+ end
11
+
12
+ def titlecase
13
+ swrgxp = self.class.small_words.join('|')
14
+
15
+ parts = self.split( %r/( [:.;?!][ ] | (?:[ ]|^)["“] )/x )
16
+ parts.each do |part|
17
+ part.gsub!(%r/\b[[:alpha:]][[:lower:].'’]*\b/) do |s|
18
+ s =~ %r/\w+\.\w+/ ? s : s.capitalize
19
+ end
20
+
21
+ # Lowercase the small words
22
+ part.gsub!(%r/\b(#{swrgxp})\b/i) {|w| w.downcase}
23
+
24
+ # If the first word is a small word, then capitalize it
25
+ part.gsub!(%r/\A([[:punct:]]*)(#{swrgxp})\b/) {$1 + $2.capitalize}
26
+
27
+ # If the last word is a small word, then capitalize it
28
+ part.gsub!(%r/\b(#{swrgxp})([^\w\s]*)\z/) {$1.capitalize + $2}
29
+ end
30
+
31
+ str = parts.join
32
+
33
+ # Special cases:
34
+ str.gsub!(/ V(s?)\. /, ' v\1. ') # "v." and "vs."
35
+ str.gsub!(/(['’])S\b/, '\1s') # 'S (otherwise you get "the SEC'S decision")
36
+ str.gsub!(/\b(AT&T|Q&A)\b/i) { |w| w.upcase } # "AT&T" and "Q&A", which get tripped up.
37
+
38
+ str
39
+ end
40
+
41
+ # Borrowed from the excellent StringEx library: git://github.com/rsl/stringex.git
42
+
43
+ # Create a URI-friendly representation of the string.
44
+ def to_url
45
+ remove_formatting.downcase.replace_whitespace("-").collapse("-")
46
+ end
47
+
48
+ # Performs multiple text manipulations. Essentially a shortcut for typing them all. View source
49
+ # below to see which methods are run.
50
+ def remove_formatting
51
+ strip_html_tags.convert_accented_entities.convert_misc_entities.convert_misc_characters.collapse
52
+ end
53
+
54
+ # Removes HTML tags from text. This code is simplified from Tobias Luettke's regular expression
55
+ # in Typo[http://typosphere.org].
56
+ def strip_html_tags(leave_whitespace = false)
57
+ name = /[\w:_-]+/
58
+ value = /([A-Za-z0-9]+|('[^']*?'|"[^"]*?"))/
59
+ attr = /(#{name}(\s*=\s*#{value})?)/
60
+ rx = /<[!\/?\[]?(#{name}|--)(\s+(#{attr}(\s+#{attr})*))?\s*([!\/?\]]+|--)?>/
61
+ (leave_whitespace) ? gsub(rx, "").strip : gsub(rx, "").gsub(/\s+/, " ").strip
62
+ end
63
+
64
+ # Converts HTML entities into the respective non-accented letters. Examples:
65
+ #
66
+ # "&aacute;".convert_accented_entities # => "a"
67
+ # "&ccedil;".convert_accented_entities # => "c"
68
+ # "&egrave;".convert_accented_entities # => "e"
69
+ # "&icirc;".convert_accented_entities # => "i"
70
+ # "&oslash;".convert_accented_entities # => "o"
71
+ # "&uuml;".convert_accented_entities # => "u"
72
+ #
73
+ # Note: This does not do any conversion of Unicode/Ascii accented-characters. For that
74
+ # functionality please use <tt>to_ascii</tt>.
75
+ def convert_accented_entities
76
+ gsub(/&([A-Za-z])(grave|acute|circ|tilde|uml|ring|cedil|slash);/, '\1')
77
+ end
78
+
79
+ # Converts HTML entities (taken from common Textile/RedCloth formattings) into plain text formats.
80
+ #
81
+ # Note: This isn't an attempt at complete conversion of HTML entities, just those most likely
82
+ # to be generated by Textile.
83
+ def convert_misc_entities
84
+ dummy = dup
85
+ {
86
+ "#822[01]" => "\"",
87
+ "#821[67]" => "'",
88
+ "#8230" => "...",
89
+ "#8211" => "-",
90
+ "#8212" => "--",
91
+ "#215" => "x",
92
+ "gt" => ">",
93
+ "lt" => "<",
94
+ "(#8482|trade)" => "(tm)",
95
+ "(#174|reg)" => "(r)",
96
+ "(#169|copy)" => "(c)",
97
+ "(#38|amp)" => "and",
98
+ "nbsp" => " ",
99
+ "(#162|cent)" => " cent",
100
+ "(#163|pound)" => " pound",
101
+ "(#188|frac14)" => "one fourth",
102
+ "(#189|frac12)" => "half",
103
+ "(#190|frac34)" => "three fourths",
104
+ "(#176|deg)" => " degrees"
105
+ }.each do |textiled, normal|
106
+ dummy.gsub!(/&#{textiled};/, normal)
107
+ end
108
+ dummy.gsub(/&[^;]+;/, "")
109
+ end
110
+
111
+ # Converts various common plaintext characters to a more URI-friendly representation.
112
+ # Examples:
113
+ #
114
+ # "foo & bar".convert_misc_characters # => "foo and bar"
115
+ # "Chanel #9".convert_misc_characters # => "Chanel number nine"
116
+ # "user@host".convert_misc_characters # => "user at host"
117
+ # "google.com".convert_misc_characters # => "google dot com"
118
+ # "$10".convert_misc_characters # => "10 dollars"
119
+ # "*69".convert_misc_characters # => "star 69"
120
+ # "100%".convert_misc_characters # => "100 percent"
121
+ # "windows/mac/linux".convert_misc_characters # => "windows slash mac slash linux"
122
+ #
123
+ # Note: Because this method will convert any & symbols to the string "and",
124
+ # you should run any methods which convert HTML entities (convert_html_entities and convert_misc_entities)
125
+ # before running this method.
126
+ def convert_misc_characters
127
+ dummy = dup.gsub(/\.{3,}/, " dot dot dot ") # Catch ellipses before single dot rule!
128
+ {
129
+ /\s*&\s*/ => "and",
130
+ /\s*#/ => "number",
131
+ /\s*@\s*/ => "at",
132
+ /(\S|^)\.(\S)/ => '\1 dot \2',
133
+ /(\s|^)\$(\d*)(\s|$)/ => '\2 dollars',
134
+ /\s*\*\s*/ => "star",
135
+ /\s*%\s*/ => "percent",
136
+ /\s*(\\|\/)\s*/ => "slash",
137
+ }.each do |found, replaced|
138
+ replaced = " #{replaced} " unless replaced =~ /\\1/
139
+ dummy.gsub!(found, replaced)
140
+ end
141
+ dummy = dummy.gsub(/(^|\w)'(\w|$)/, '\1\2').gsub(/[\.,:;()\[\]\/\?!\^'"_]/, " ")
142
+ end
143
+
144
+ # Replace runs of whitespace in string. Defaults to a single space but any replacement
145
+ # string may be specified as an argument. Examples:
146
+ #
147
+ # "Foo bar".replace_whitespace # => "Foo bar"
148
+ # "Foo bar".replace_whitespace("-") # => "Foo-bar"
149
+ def replace_whitespace(replace = " ")
150
+ gsub(/\s+/, replace)
151
+ end
152
+
153
+ # Removes specified character from the beginning and/or end of the string and then performs
154
+ # <tt>String#squeeze(character)</tt>, condensing runs of the character within the string.
155
+ #
156
+ # Note: This method has been superceded by ActiveSupport's squish method.
157
+ def collapse(character = " ")
158
+ sub(/^#{character}*/, "").sub(/#{character}*$/, "").squeeze(character)
159
+ end
160
+
161
+ end # class String
162
+
163
+ # EOF
@@ -0,0 +1,9 @@
1
+
2
+ class Time
3
+
4
+ def to_y
5
+ self.to_yaml.slice(4..-1).strip
6
+ end
7
+ end # class Time
8
+
9
+ # EOF
@@ -1,10 +1,18 @@
1
- try_require 'haml'
2
1
 
3
2
  # Render text via the Haml library
4
- Webby::Filters.register :haml do |input, cursor|
5
- opts = ::Webby.site.haml_options.merge(cursor.page.haml_options || {})
6
- b = cursor.renderer.get_binding
7
- Haml::Engine.new(input, opts).to_html(b)
3
+ if try_require('haml', 'haml')
4
+
5
+ Webby::Filters.register :haml do |input, cursor|
6
+ opts = ::Webby.site.haml_options.merge(cursor.page.haml_options || {})
7
+ b = cursor.renderer.get_binding
8
+ Haml::Engine.new(input, opts).to_html(b)
9
+ end
10
+
11
+ # Otherwise raise an error if the user tries to use haml
12
+ else
13
+ Webby::Filters.register :haml do |input, cursor|
14
+ raise Webby::Error, "'haml' must be installed to use the haml filter"
15
+ end
8
16
  end
9
17
 
10
18
  # EOF
@@ -1,8 +1,16 @@
1
- try_require 'bluecloth'
2
1
 
3
- # Render text via markdown using the BlueCloth library.
4
- Webby::Filters.register :markdown do |input|
5
- BlueCloth.new(input).to_html
2
+ # Render text via markdown using the RDiscount library.
3
+ if try_require('rdiscount', 'rdiscount')
4
+
5
+ Webby::Filters.register :markdown do |input|
6
+ RDiscount.new(input).to_html
7
+ end
8
+
9
+ # Otherwise raise an error if the user tries to use markdown
10
+ else
11
+ Webby::Filters.register :markdown do |input|
12
+ raise Webby::Error, "'rdiscount' must be installed to use the markdown filter"
13
+ end
6
14
  end
7
15
 
8
16
  # EOF
@@ -112,7 +112,7 @@ class Outline
112
112
  # the table of contents -- "Get Fuzzy" in this example.
113
113
  #
114
114
  def filter
115
- doc = Hpricot(@str)
115
+ doc = Hpricot.XML(@str)
116
116
 
117
117
  # extract directives from the "toc" tag
118
118
  toc_elem = doc.search('toc').first
@@ -132,7 +132,8 @@ class Outline
132
132
  @toc_range = Integer(m[1])..Integer(m[2])
133
133
  @list_opening = build_list_opening(toc_elem)
134
134
 
135
- doc.traverse_element(*%w[h1 h2 h3 h4 h5 h6]) do |elem|
135
+ headers = @toc_range.map {|x| "h#{x}"}
136
+ doc.traverse_element(*headers) do |elem|
136
137
  text, id = heading_info(elem)
137
138
  add_to_toc(text, id) if @toc_range.include? current_level
138
139
  end
@@ -1,9 +1,17 @@
1
- try_require 'sass'
2
1
 
3
2
  # Render text via the Sass library (part of Haml)
4
- Webby::Filters.register :sass do |input, cursor|
5
- opts = ::Webby.site.sass_options.merge(cursor.page.sass_options || {})
6
- Sass::Engine.new(input, opts).render
3
+ if try_require('sass', 'haml')
4
+
5
+ Webby::Filters.register :sass do |input, cursor|
6
+ opts = ::Webby.site.sass_options.merge(cursor.page.sass_options || {})
7
+ Sass::Engine.new(input, opts).render
8
+ end
9
+
10
+ # Otherwise raise an error if the user tries to use sass
11
+ else
12
+ Webby::Filters.register :sass do |input, cursor|
13
+ raise Webby::Error, "'haml' must be installed to use the sass filter"
14
+ end
7
15
  end
8
16
 
9
17
  # EOF
@@ -1,8 +1,16 @@
1
- try_require 'redcloth'
2
1
 
3
- # Render text via textile using the RedCloth library.
4
- Webby::Filters.register :textile do |input|
5
- RedCloth.new(input, %w(no_span_caps)).to_html
2
+ # If RedCloth is installed, then configure the textile filter
3
+ if try_require('redcloth', 'RedCloth')
4
+
5
+ Webby::Filters.register :textile do |input|
6
+ RedCloth.new(input, %w(no_span_caps)).to_html
7
+ end
8
+
9
+ # Otherwise raise an error if the user tries to use textile
10
+ else
11
+ Webby::Filters.register :textile do |input|
12
+ raise Webby::Error, "'RedCloth' must be installed to use the textile filter"
13
+ end
6
14
  end
7
15
 
8
16
  # EOF
@@ -41,11 +41,6 @@ class Tidy
41
41
  # created and output Tidy formatted HTML or XHTML.
42
42
  #
43
43
  def process
44
- %x[tidy -v 2>&1]
45
- unless 0 == $?.exitstatus
46
- raise NameError, "'tidy' not found on the path"
47
- end
48
-
49
44
  cmd = "tidy %s -q -f #{@err.path}" % ::Webby.site.tidy_options
50
45
  out = IO.popen(cmd, 'r+') do |tidy|
51
46
  tidy.write @str
@@ -63,9 +58,16 @@ class Tidy
63
58
  end # class Tidy
64
59
 
65
60
  # Render html into html/xhtml via the Tidy program
66
- #
67
- register :tidy do |input|
68
- Filters::Tidy.new(input).process
61
+ if cmd_available? %w[tidy -v]
62
+ register :tidy do |input|
63
+ Filters::Tidy.new(input).process
64
+ end
65
+
66
+ # Otherwise raise an error if the user tries to use tidy
67
+ else
68
+ register :tidy do |input|
69
+ raise Webby::Error, "'tidy' must be installed to use the tidy filter"
70
+ end
69
71
  end
70
72
 
71
73
  end # module Filters
data/lib/webby/filters.rb CHANGED
@@ -50,6 +50,8 @@ module Filters
50
50
  @renderer.instance_variable_set(:@_cursor, self)
51
51
  filters.inject(input) do |result, filter|
52
52
  handler = Filters[filter]
53
+ raise ::Webby::Error, "unknown filter: #{filter.inspect}" if handler.nil?
54
+
53
55
  args = [result, self][0, handler.arity]
54
56
  handle(filter, handler, *args)
55
57
  end
@@ -127,8 +127,7 @@ module GraphvizHelper
127
127
  end
128
128
  end # module GraphvizHelper
129
129
 
130
- %x[dot -V 2>&1]
131
- if 0 == $?.exitstatus
130
+ if cmd_available? %w[dot -V]
132
131
  register(GraphvizHelper)
133
132
  end
134
133