mango 0.5.4 → 0.6.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 (83) hide show
  1. data/.gitignore +1 -2
  2. data/.rvmrc +1 -0
  3. data/.yardopts +1 -3
  4. data/{CHANGES.mdown → CHANGES.md} +40 -0
  5. data/Gemfile.lock +35 -23
  6. data/LICENSE +1 -1
  7. data/{README.mdown → README.md} +38 -27
  8. data/Rakefile +0 -13
  9. data/lib/mango.rb +5 -0
  10. data/lib/mango/application.rb +361 -129
  11. data/lib/mango/content_page.rb +89 -115
  12. data/lib/mango/core_ext/string.rb +16 -0
  13. data/lib/mango/core_ext/uri.rb +14 -0
  14. data/lib/mango/dependencies.rb +5 -4
  15. data/lib/mango/flavored_markdown.rb +7 -0
  16. data/lib/mango/rack/debugger.rb +20 -0
  17. data/lib/mango/runner.rb +33 -50
  18. data/lib/mango/templates/.gitignore +0 -1
  19. data/lib/mango/templates/Gemfile +1 -1
  20. data/lib/mango/templates/content/index.erb +6 -0
  21. data/lib/mango/templates/themes/default/javascripts/timer.coffee +6 -0
  22. data/lib/mango/templates/themes/default/public/javascripts/fireworks.js +0 -0
  23. data/lib/mango/templates/themes/default/public/{styles → stylesheets}/fireworks.css +0 -0
  24. data/lib/mango/templates/themes/default/public/{styles → stylesheets}/reset.css +0 -2
  25. data/lib/mango/templates/themes/default/{styles → stylesheets}/screen.sass +0 -2
  26. data/lib/mango/templates/themes/default/views/layout.haml +5 -5
  27. data/lib/mango/templates/themes/default/views/page.haml +1 -3
  28. data/lib/mango/version.rb +1 -1
  29. data/mango.gemspec +12 -10
  30. data/spec/fixture/content/engines/erb.erb +7 -0
  31. data/spec/fixture/content/engines/haml.haml +4 -4
  32. data/spec/fixture/content/engines/liquid.liquid +7 -0
  33. data/spec/fixture/content/engines/markdown.markdown +0 -6
  34. data/spec/fixture/content/engines/md.md +0 -6
  35. data/spec/fixture/content/engines/mkd.mkd +1 -0
  36. data/spec/fixture/content/page_with_missing_view.haml +2 -2
  37. data/spec/fixture/content/page_with_unregistered_view.haml +4 -0
  38. data/spec/fixture/content/view_engines/erb.haml +4 -0
  39. data/spec/fixture/content/view_engines/liquid.haml +4 -0
  40. data/spec/fixture/themes/default/javascripts/override.coffee +1 -0
  41. data/spec/fixture/themes/default/javascripts/siblings.coffee +7 -0
  42. data/spec/fixture/themes/default/javascripts/songs/happy.coffee +5 -0
  43. data/spec/fixture/themes/default/public/404.html +11 -0
  44. data/spec/fixture/themes/default/public/javascripts/econ.js +8 -0
  45. data/spec/fixture/themes/default/public/javascripts/math/opposite.js +6 -0
  46. data/spec/fixture/themes/default/public/javascripts/override.js +1 -0
  47. data/spec/fixture/themes/default/public/root.js +5 -0
  48. data/spec/fixture/themes/default/public/{styles → stylesheets}/override.css +0 -0
  49. data/spec/fixture/themes/default/public/{styles → stylesheets}/reset.css +0 -0
  50. data/spec/fixture/themes/default/public/{styles → stylesheets}/subfolder/another.css +0 -0
  51. data/spec/fixture/themes/default/security_hole.js +1 -0
  52. data/spec/fixture/themes/default/{styles → stylesheets}/override.sass +0 -0
  53. data/spec/fixture/themes/default/{styles/screen.sass → stylesheets/sass.sass} +4 -2
  54. data/spec/fixture/themes/default/stylesheets/scss.scss +19 -0
  55. data/spec/fixture/themes/default/{styles → stylesheets}/subfolder/screen.sass +0 -2
  56. data/spec/fixture/themes/default/views/404.erb +11 -0
  57. data/spec/fixture/themes/default/views/404.haml +1 -0
  58. data/spec/fixture/themes/default/views/404.liquid +11 -0
  59. data/spec/fixture/themes/default/views/layout.erb +10 -0
  60. data/spec/fixture/themes/default/views/layout.haml +1 -1
  61. data/spec/fixture/themes/default/views/layout.liquid +10 -0
  62. data/spec/fixture/themes/default/views/page.erb +7 -0
  63. data/spec/fixture/themes/default/views/page.haml +3 -1
  64. data/spec/fixture/themes/default/views/page.liquid +7 -0
  65. data/spec/lib/application/routing_content_pages_spec.rb +298 -21
  66. data/spec/lib/application/routing_javascript_templates_spec.rb +278 -0
  67. data/spec/lib/application/routing_not_found_spec.rb +254 -0
  68. data/spec/lib/application/routing_public_files_spec.rb +9 -8
  69. data/spec/lib/application/{routing_style_sheets_spec.rb → routing_stylesheet_templates_spec.rb} +61 -24
  70. data/spec/lib/application_spec.rb +31 -2
  71. data/spec/lib/content_page/initializing_spec.rb +399 -126
  72. data/spec/lib/content_page_spec.rb +23 -19
  73. data/spec/lib/core_ext/string_spec.rb +12 -0
  74. data/spec/lib/core_ext/uri_spec.rb +42 -0
  75. data/spec/lib/dependencies_spec.rb +0 -1
  76. data/spec/lib/flavored_markdown_spec.rb +18 -3
  77. data/spec/lib/runner_spec.rb +40 -39
  78. data/spec/lib/version_spec.rb +4 -4
  79. metadata +145 -64
  80. data/lib/mango/templates/content/index.md +0 -5
  81. data/lib/mango/templates/themes/default/public/javascripts/timer.js +0 -5
  82. data/spec/fixture/content/engines/mdown.mdown +0 -7
  83. data/spec/lib/content_page/finding_spec.rb +0 -213
data/.gitignore CHANGED
@@ -1,7 +1,6 @@
1
1
  .DS_Store
2
2
  .bundle
3
- .rvmrc
3
+ .sass-cache
4
4
  .yardoc
5
5
  *.gem
6
6
  *.tmproj
7
- yardoc
data/.rvmrc ADDED
@@ -0,0 +1 @@
1
+ rvm ruby-1.9.2-p180@mango
data/.yardopts CHANGED
@@ -1,6 +1,4 @@
1
- --output-dir ./yardoc
2
1
  --markup markdown
3
- --plugin yard-sinatra
4
2
  -
5
3
  LICENSE
6
- CHANGES.mdown
4
+ CHANGES.md
@@ -1,5 +1,45 @@
1
1
  # CHANGES
2
2
 
3
+ ## v0.6.0 / 2011-05-29
4
+
5
+ [Full changes](https://github.com/ryansobol/mango/compare/v0.5.4...v0.6.0)
6
+
7
+ ## Features
8
+
9
+ * Add ERB view template support [GH#21](https://github.com/ryansobol/mango/issues/21)
10
+ * Add ERB 404 template support [GH#26](https://github.com/ryansobol/mango/issues/26)
11
+ * Add 404 default response [GH#27](https://github.com/ryansobol/mango/issues/27)
12
+ * Add 404.html public file support [GH#28](https://github.com/ryansobol/mango/issues/28)
13
+ * Add Scss stylesheet template support [GH#31](https://github.com/ryansobol/mango/issues/31)
14
+ * Add ERB content page support [GH#33](https://github.com/ryansobol/mango/issues/33)
15
+ * Use ContentPage as local `page` variable within content page and view templates [GH#34](https://github.com/ryansobol/mango/issues/34)
16
+ * Add Liquid content page, view template, and 404 template support [GH#40](https://github.com/ryansobol/mango/issues/40)
17
+ * Add CoffeeScript support and JavaScript route handler [GH#36](https://github.com/ryansobol/mango/issues/36)
18
+
19
+ ### Dependencies
20
+
21
+ * Update Sinatra to ~> 1.2.6 [GH#14](https://github.com/ryansobol/mango/issues/14)
22
+ * Update RSpec to ~> 2.6.0 [GH#32](https://github.com/ryansobol/mango/issues/32)
23
+ * Add Sass ~> 3.1.1 [GH#24](https://github.com/ryansobol/mango/issues/24)
24
+ * Update Haml to ~> 3.1.1 [GH#23](https://github.com/ryansobol/mango/issues/23)
25
+ * Update Rack::Test to ~> 0.6.0 [GH#25](https://github.com/ryansobol/mango/issues/25)
26
+ * Update YARD to ~> 0.7.1 [GH#38](https://github.com/ryansobol/mango/issues/38)
27
+
28
+ ### Chores
29
+
30
+ * Unignore .rvmrc [GH#16](https://github.com/ryansobol/mango/issues/16)
31
+ * Move `Mango::Application#directory_path?` to `URI.directory?` [GH#29](https://github.com/ryansobol/mango/issues/29)
32
+ * Move `File.templatize` to `String#templatize` and simplify usage [GH#30](https://github.com/ryansobol/mango/issues/30)
33
+ * Rename all `.mdown` files to `.md` [GH#37](https://github.com/ryansobol/mango/issues/37)
34
+ * Remove the `yard` task from the Rakefile [GH#6](https://github.com/ryansobol/mango/issues/6)
35
+ * Add spec coverage for 404.liquid route handling [GH#42](https://github.com/ryansobol/mango/issues/42)
36
+ * Update rspec-core to 2.6.2 [GH#41](https://github.com/ryansobol/mango/issues/41)
37
+ * Update tilt to 1.3.1 [GH#43](https://github.com/ryansobol/mango/issues/43)
38
+ * Update LICENSE copyright to 2011 [GH#47](https://github.com/ryansobol/mango/issues/47)
39
+ * Update rspec-core to 2.6.3 [GH#46](https://github.com/ryansobol/mango/issues/46)
40
+ * Update Tilt to 1.3.2 [GH#45](https://github.com/ryansobol/mango/issues/45)
41
+ * Update multi_json to 1.0.3 [GH#53](https://github.com/ryansobol/mango/issues/53)
42
+
3
43
  ## v0.5.4 / 2011-04-24
4
44
 
5
45
  [Full changes](https://github.com/ryansobol/mango/compare/v0.5.3...v0.5.4)
@@ -1,39 +1,49 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- mango (0.5.4)
4
+ mango (0.6.0)
5
5
  bluecloth (~> 2.1.0)
6
6
  bundler (~> 1.0.7)
7
- haml (~> 3.0.25)
7
+ coffee-script (~> 2.2.0)
8
+ haml (~> 3.1.1)
9
+ liquid (~> 2.2.2)
8
10
  rack (~> 1.2.2)
9
- sinatra (~> 1.1.4)
11
+ sass (~> 3.1.1)
12
+ sinatra (~> 1.2.6)
10
13
  thor (~> 0.14.6)
11
14
 
12
15
  GEM
13
16
  remote: http://rubygems.org/
14
17
  specs:
15
18
  bluecloth (2.1.0)
19
+ coffee-script (2.2.0)
20
+ coffee-script-source
21
+ execjs
22
+ coffee-script-source (1.1.1)
16
23
  diff-lcs (1.1.2)
17
- haml (3.0.25)
24
+ execjs (1.0.0)
25
+ multi_json (~> 1.0)
26
+ haml (3.1.1)
27
+ liquid (2.2.2)
28
+ multi_json (1.0.3)
18
29
  rack (1.2.2)
19
- rack-test (0.5.7)
30
+ rack-test (0.6.0)
20
31
  rack (>= 1.0)
21
- rspec (2.5.0)
22
- rspec-core (~> 2.5.0)
23
- rspec-expectations (~> 2.5.0)
24
- rspec-mocks (~> 2.5.0)
25
- rspec-core (2.5.1)
26
- rspec-expectations (2.5.0)
32
+ rspec (2.6.0)
33
+ rspec-core (~> 2.6.0)
34
+ rspec-expectations (~> 2.6.0)
35
+ rspec-mocks (~> 2.6.0)
36
+ rspec-core (2.6.3)
37
+ rspec-expectations (2.6.0)
27
38
  diff-lcs (~> 1.1.2)
28
- rspec-mocks (2.5.0)
29
- sinatra (1.1.4)
39
+ rspec-mocks (2.6.0)
40
+ sass (3.1.1)
41
+ sinatra (1.2.6)
30
42
  rack (~> 1.1)
31
43
  tilt (>= 1.2.2, < 2.0)
32
44
  thor (0.14.6)
33
- tilt (1.2.2)
34
- yard (0.6.8)
35
- yard-sinatra (0.5.1)
36
- yard (~> 0.6.0)
45
+ tilt (1.3.2)
46
+ yard (0.7.1)
37
47
 
38
48
  PLATFORMS
39
49
  ruby
@@ -41,12 +51,14 @@ PLATFORMS
41
51
  DEPENDENCIES
42
52
  bluecloth (~> 2.1.0)
43
53
  bundler (~> 1.0.7)
44
- haml (~> 3.0.25)
54
+ coffee-script (~> 2.2.0)
55
+ haml (~> 3.1.1)
56
+ liquid (~> 2.2.2)
45
57
  mango!
46
58
  rack (~> 1.2.2)
47
- rack-test (~> 0.5.7)
48
- rspec (~> 2.5.0)
49
- sinatra (~> 1.1.4)
59
+ rack-test (~> 0.6.0)
60
+ rspec (~> 2.6.0)
61
+ sass (~> 3.1.1)
62
+ sinatra (~> 1.2.6)
50
63
  thor (~> 0.14.6)
51
- yard (~> 0.6.8)
52
- yard-sinatra (~> 0.5.1)
64
+ yard (~> 0.7.1)
data/LICENSE CHANGED
@@ -1,4 +1,4 @@
1
- Copyright (c) 2010 Ryan Sobol
1
+ Copyright (c) 2011 Ryan Sobol
2
2
 
3
3
  Permission is hereby granted, free of charge, to any person obtaining
4
4
  a copy of this software and associated documentation files (the
@@ -1,5 +1,5 @@
1
- Mango release 0.5.4 (April 24, 2011)
2
- ====================================
1
+ Mango release 0.6.0 (May 29, 2011)
2
+ ==================================
3
3
 
4
4
  Copyright (c) 2010 Ryan Sobol. Licensed under the MIT license. Please see the {file:LICENSE} for more information.
5
5
 
@@ -27,17 +27,26 @@ Mango integrates with the writing tools you're already familiar with -- the file
27
27
 
28
28
  Working with a text editor you already know *saves time*. And writing and revising content in a file *is often easier* then using the clunky administrator interface of a CMS (e.g. WordPress). Plus, files *match perfectly* with version control systems, like [Git](http://git-scm.com/), for powerful revision history. Bonus!
29
29
 
30
+ Mango supports the following *easy to write* content formats:
31
+
32
+ * [Markdown](http://daringfireball.net/projects/markdown/basics)
33
+ * [Haml](http://haml-lang.com/tutorial.html)
34
+ * [ERB](http://ruby-doc.org/stdlib/libdoc/erb/rdoc/classes/ERB.html)
35
+ * [Liquid](https://github.com/tobi/liquid/wiki)
36
+
37
+ Don't see your favorite content format? [Patches are welcomed](https://github.com/ryansobol/mango/issues)
38
+
30
39
  ### Easy to theme
31
40
 
32
- Mango separates a website's theme from it's content. This makes it *easy to change* the look-and-feel of a single page or an entire website. Mango also supports powerful template languages that make it *easy to have uniformity* with major sections (i.e. headers, footers, navigation, sidebars, etc.) across multiple pages. The following web and template formats are supported by Mango:
41
+ Mango separates a website's theme from it's content. This makes it *easy to change* the look-and-feel of a single page or an entire website. Mango also supports powerful template languages that make it *easy to have uniformity* with major sections (i.e. headers, footers, navigation, sidebars, etc.) across multiple pages. In addition to the standard browser formats -- HTML, CSS, and JavaScript -- Mango also supports the following template engines:
33
42
 
34
- * HAML
35
- * HTML
36
- * SASS
37
- * CSS
38
- * JavaScript
43
+ * [Haml](http://haml-lang.com/)
44
+ * [ERB](http://ruby-doc.org/stdlib/libdoc/erb/rdoc/classes/ERB.html)
45
+ * [Liquid](https://github.com/tobi/liquid/wiki)
46
+ * [Scss](http://sass-lang.com/) and [Sass](http://sass-lang.com/)
47
+ * [CoffeeScript](http://jashkenas.github.com/coffee-script/)
39
48
 
40
- with [more](https://github.com/sinatra/sinatra/blob/1.1.0/CHANGES#L12) on the way.
49
+ Don't see your favorite template engine? [Patches are welcomed](https://github.com/ryansobol/mango/issues)
41
50
 
42
51
  ### Easy to publish
43
52
 
@@ -53,7 +62,7 @@ With a strong separation of writing, theming and publishing, Mango applications
53
62
 
54
63
  ### Easy to maintain
55
64
 
56
- Mango is a database-less web framework. With no database to install, configure, or manage, the maintenance needed to keep your website in working order is *drastically reduced*. And because Mango is distributed as a RubyGem and respects [Semantic Versioning](http://semver.org/), *upgrading is painless andi backwards-compatible* between patch releases.
65
+ Mango is a database-less web framework. With no database to install, configure, or manage, the maintenance needed to keep your website in working order is *drastically reduced*. And because Mango is distributed as a RubyGem and respects [Semantic Versioning](http://semver.org/), *upgrading is painless and backwards-compatible* between patch releases.
57
66
 
58
67
  ### Easy to scale
59
68
 
@@ -74,18 +83,20 @@ REQUIREMENTS
74
83
  * [RubyGems](https://rubygems.org/) >= 1.3.7 (bundled with Ruby)
75
84
  * [Bundler](http://gembundler.com/) ~> 1.0.7
76
85
  * [Rack](http://rack.rubyforge.org/) ~> 1.2.2
77
- * [Sinatra](http://www.sinatrarb.com/) ~> 1.1.4
78
- * [Haml](http://haml-lang.com/) ~> 3.0.25
79
- * [Sass](http://sass-lang.com/) ~> 3.0.25 (bundled with Haml)
86
+ * [Sinatra](http://www.sinatrarb.com/) ~> 1.2.6
87
+ * [Haml](http://haml-lang.com/) ~> 3.1.1
88
+ * [Sass](http://sass-lang.com/) ~> 3.1.1
80
89
  * [BlueCloth](http://deveiate.org/projects/BlueCloth) ~> 2.1.0
90
+ * [Liquid](http://www.liquidmarkup.org/) ~> 2.2.2
91
+ * [CoffeeScript](http://jashkenas.github.com/coffee-script/) ~> 2.2.0
81
92
  * [Thor](https://github.com/wycats/thor) ~> 0.14.6
82
93
 
83
94
  ### Optional development dependencies
84
95
 
85
96
  * [Rake](http://rake.rubyforge.org/) ~> 0.8.7 (bundled with Ruby)
86
- * [Rack::Test](https://github.com/brynary/rack-test) ~> 0.5.7
87
- * [RSpec](http://rspec.info/) ~> 2.5.0
88
- * [YARD](http://yardoc.org/) ~> 0.6.8
97
+ * [Rack::Test](https://github.com/brynary/rack-test) ~> 0.6.0
98
+ * [RSpec](http://rspec.info/) ~> 2.6.0
99
+ * [YARD](http://yardoc.org/) ~> 0.7.1
89
100
  * [YARD::Sinatra](https://github.com/rkh/yard-sinatra) ~> 0.5.1
90
101
  * [BlueCloth](http://deveiate.org/projects/BlueCloth) ~> 2.1.0
91
102
 
@@ -117,7 +128,7 @@ Simply edit the gem version in your application's `Gemfile` and re-install with
117
128
  $ cat Gemfile
118
129
  # encoding: UTF-8
119
130
  source "http://rubygems.org"
120
- gem "mango", "~> 0.5.4"
131
+ gem "mango", "~> 0.6.0"
121
132
  $ bundle install
122
133
 
123
134
  **TIP:** If you're working in the insolation of an [RVM gemset](http://rvm.beginrescueend.com/gemsets/basics/), type `gem clean` to uninstall outdated gems.
@@ -166,21 +177,22 @@ Now that the newly generated Mango application is running, here's how the applic
166
177
  ├── README.md
167
178
  ├── config.ru
168
179
  ├── content
169
- │ └── index.md
180
+ │ └── index.erb
170
181
  └── themes
171
182
  └── default
183
+ ├── javascripts
184
+ │ └── timer.coffee
172
185
  ├── public
173
186
  │ ├── favicon.ico
174
187
  │ ├── images
175
188
  │ │ └── particles.gif
176
189
  │ ├── javascripts
177
- │ │ ├── fireworks.js
178
- │ │ └── timer.js
190
+ │ │ └── fireworks.js
179
191
  │ ├── robots.txt
180
- │ └── styles
192
+ │ └── stylesheets
181
193
  │ ├── fireworks.css
182
194
  │ └── reset.css
183
- ├── styles
195
+ ├── stylesheets
184
196
  │ └── screen.sass
185
197
  └── views
186
198
  ├── 404.haml
@@ -193,11 +205,10 @@ Now that the newly generated Mango application is running, here's how the applic
193
205
 
194
206
  * First, Mango tries to route an HTTP request to a static file found in `themes/default/public/`.
195
207
  * If no static file is found, Mango tries to route the request to a template file.
196
- * For routes ending in `.css`, Mango searches for [Sass](http://sass-lang.com/) styles found in `themes/styles/`.
197
- * For all other routes:
198
- * Mango searches for [Markdown](http://daringfireball.net/projects/markdown/syntax) or [Haml](http://haml-lang.com/) content pages in `content/`.
199
- * Mango will also try to wrap the content page within [Haml](http://haml-lang.com/) page and layout templates in `themes/default/views`.
200
- * If no static or template file is found, Mango routes unknown HTTP requests to a customizable 404 page found in `themes/default/views`.
208
+ * For routes ending in `.js`, Mango searches for a stylesheet template in `themes/javascripts/`.
209
+ * For routes ending in `.css`, Mango searches for a stylesheet template in `themes/stylesheets/`.
210
+ * For all other routes, Mango searches for a content page in `content/` and wraps it within a view template in `themes/default/views`.
211
+ * If no static or template file is found, Mango routes unknown HTTP requests to a customizable 404 page found in either `themes/default/public` or `themes/default/views`.
201
212
 
202
213
  WRITING
203
214
  -------
data/Rakefile CHANGED
@@ -15,19 +15,6 @@ end
15
15
 
16
16
  ###################################################################################################
17
17
 
18
- begin
19
- require "yard"
20
- require "bluecloth" # for Markdown support
21
- require "yard/sinatra" # for Sinatra support
22
- YARD::Rake::YardocTask.new(:yard) do |t|
23
- t.options += ["--title", "Mango #{Mango::VERSION} Documentation"]
24
- end
25
- rescue LoadError => e
26
- Mango::Dependencies.create_warning_for(e)
27
- end
28
-
29
- ###################################################################################################
30
-
31
18
  namespace :gem do
32
19
  desc "Builds a gem from the current project's Gem::Specification"
33
20
  task :build do
@@ -4,7 +4,12 @@
4
4
  require File.expand_path("mango/dependencies", File.dirname(__FILE__))
5
5
 
6
6
  require_relative "mango/version"
7
+ require_relative "mango/core_ext/string"
8
+ require_relative "mango/core_ext/uri"
7
9
  require_relative "mango/rack/debugger"
8
10
  require_relative "mango/application"
9
11
  require_relative "mango/flavored_markdown"
10
12
  require_relative "mango/content_page"
13
+
14
+ # @see http://en.wikipedia.org/wiki/Mango
15
+ module Mango; end
@@ -2,6 +2,10 @@
2
2
  require "sinatra/base"
3
3
  require "haml"
4
4
  require "sass"
5
+ require "erb"
6
+ require "liquid"
7
+ require "bluecloth"
8
+ require "coffee_script"
5
9
 
6
10
  module Mango
7
11
  # It's probably no surprise that `Mango::Application` is a modular **application controller**
@@ -12,10 +16,11 @@ module Mango
12
16
  # For **every HTTP request**, the application will first attempt to match the request URI path to
13
17
  # a public file found within `settings.public` and send that file with a 200 response code.
14
18
  #
15
- # In addition to serving static assets, the application has two dynamic route handlers:
19
+ # In addition to serving static assets, the application has these dynamic route handlers:
16
20
  #
17
21
  # * Content page templates with `GET *`
18
- # * Style sheet templates with `GET /styles/*.css`
22
+ # * JavaScript templates with `GET /javascripts/*.js`
23
+ # * Stylesheet templates with `GET /stylesheets/*.css`
19
24
  #
20
25
  # and one error handler:
21
26
  #
@@ -23,7 +28,7 @@ module Mango
23
28
  #
24
29
  # # Serving public files found within `settings.public`
25
30
  #
26
- # ### Example requests routed to public files (with potential security holes)
31
+ # ### Example requests routed to public files
27
32
  #
28
33
  # |-- content
29
34
  # | `-- override.haml
@@ -47,86 +52,199 @@ module Mango
47
52
  #
48
53
  # # Content page templates with `GET *`
49
54
  #
50
- # ### Example `GET *` requests routed to content pages (with potential security holes)
55
+ # ### Example `GET *` requests routed to content page templates
51
56
  #
52
57
  # |-- content
53
58
  # | |-- about
54
- # | | |-- index.haml
59
+ # | | |-- index.erb
55
60
  # | | `-- us.haml
56
- # | |-- index.haml
61
+ # | |-- index.markdown
57
62
  # | |-- override.haml
58
63
  # | `-- turner+hooch.haml
59
64
  # `-- security_hole.haml
60
65
  #
61
- # GET / => 200 content/index.haml
62
- # GET /index => 200 content/index.haml
63
- # GET /index?foo=bar => 200 content/index.haml
64
- # GET /about/ => 200 content/about/index.haml
65
- # GET /about/index => 200 content/about/index.haml
66
+ # GET / => 200 content/index.markdown
67
+ # GET /index => 200 content/index.markdown
68
+ # GET /index?foo=bar => 200 content/index.markdown
69
+ # GET /about/ => 200 content/about/index.erb
70
+ # GET /about/index => 200 content/about/index.erb
66
71
  # GET /about/us => 200 content/about/us.haml
67
72
  # GET /turner%2Bhooch => 200 content/turner+hooch.haml
68
73
  #
69
74
  # GET /page_not_found => pass to NOT_FOUND error handler
70
75
  # GET /../security_hole => pass to NOT_FOUND error handler
71
76
  #
72
- # # Style sheet templates with `GET /styles/*.css`
77
+ # # JavaScript templates with `GET /javascripts/*.js`
73
78
  #
74
- # ### Example `GET /styles/*.css` requests routed to style sheets (with potential security holes)
79
+ # ### Example `GET /javascripts/*.js` requests routed to JavaScript files and templates
80
+ #
81
+ # `-- themes
82
+ # `-- default
83
+ # |-- javascripts
84
+ # | |-- override.coffee
85
+ # | |-- siblings.coffee
86
+ # | `-- songs
87
+ # | `-- happy.coffee
88
+ # |-- public
89
+ # | |-- root.js
90
+ # | `-- stylesheets
91
+ # | |-- econ.js
92
+ # | |-- math
93
+ # | | `-- opposite.js
94
+ # | `-- override.js
95
+ # `-- security_hole.js
96
+ #
97
+ # GET /javascripts/siblings.js => 200 themes/default/stylesheets/siblings.coffee
98
+ # GET /javascripts/songs/happy.js => 200 themes/default/stylesheets/songs/happy.coffee
99
+ #
100
+ # GET /javascripts/econ.js => 200 themes/default/public/javascripts/econ.js
101
+ # GET /javascripts/override.js => 200 themes/default/public/javascripts/override.js
102
+ # GET /root.js => 200 themes/default/public/root.js
103
+ # GET /javascripts/math/opposite.js => 200 themes/default/public/javascripts/math/opposite.js
104
+ #
105
+ # GET /javascripts/not_found.js => pass to NOT_FOUND error handler
106
+ # GET /siblings.js => pass to NOT_FOUND error handler
107
+ # GET /javascripts/../security_hole.js => pass to NOT_FOUND error handler
108
+ #
109
+ # # Stylesheet templates with `GET /stylesheets/*.css`
110
+ #
111
+ # ### Example `GET /stylesheets/*.css` requests routed to stylesheets files and templates
75
112
  #
76
113
  # `-- themes
77
114
  # `-- default
78
115
  # |-- public
79
116
  # | |-- default.css
80
- # | `-- styles
117
+ # | `-- stylesheets
81
118
  # | |-- override.css
82
119
  # | |-- reset.css
83
- # | `-- subfolder
84
- # | `-- another.css
120
+ # | `-- folder
121
+ # | `-- print.css
85
122
  # |-- security_hole.sass
86
- # `-- styles
123
+ # `-- stylesheets
87
124
  # |-- override.sass
88
- # |-- screen.sass
89
- # `-- subfolder
90
- # `-- screen.sass
125
+ # |-- screen.scss
126
+ # `-- folder
127
+ # `-- mobile.sass
91
128
  #
92
- # GET /styles/screen.css => 200 themes/default/styles/screen.sass
93
- # GET /styles/subfolder/screen.css => 200 themes/default/styles/subfolder/screen.sass
129
+ # GET /stylesheets/screen.css => 200 themes/default/stylesheets/screen.scss
130
+ # GET /stylesheets/folder/mobile.css => 200 themes/default/stylesheets/folder/mobile.sass
94
131
  #
95
- # GET /styles/reset.css => 200 themes/default/public/styles/reset.css
96
- # GET /styles/override.css => 200 themes/default/public/styles/override.css
132
+ # GET /stylesheets/reset.css => 200 themes/default/public/stylesheets/reset.css
133
+ # GET /stylesheets/override.css => 200 themes/default/public/stylesheets/override.css
97
134
  # GET /default.css => 200 themes/default/public/default.css
98
- # GET /styles/subfolder/another.css => 200 themes/default/public/styles/subfolder/another.css
135
+ # GET /stylesheets/folder/print.css => 200 themes/default/public/stylesheets/folder/print.css
99
136
  #
100
- # GET /styles/style_not_found.css => pass to NOT_FOUND error handler
101
- # GET /screen.css => pass to NOT_FOUND error handler
102
- # GET /styles/../security_hole.css => pass to NOT_FOUND error handler
137
+ # GET /stylesheets/not_found.css => pass to NOT_FOUND error handler
138
+ # GET /screen.css => pass to NOT_FOUND error handler
139
+ # GET /stylesheets/../security_hole.css => pass to NOT_FOUND error handler
103
140
  #
104
141
  # # 404 Page Not Found with `NOT_FOUND`
105
142
  #
106
143
  # When a requested URI path cannot be matched with a public file or template file, the error
107
- # handler renders the 404 template and sends it with a 404 response.
144
+ # handler attempts to send a 404 public file or a rendered a 404 template with a 404 HTTP
145
+ # response.
146
+ #
147
+ # ### Example `GET /page_not_found` request routed to a 404 public file
148
+ #
149
+ # `-- themes
150
+ # `-- default
151
+ # `-- public
152
+ # `-- 404.html
153
+ #
154
+ # GET /page_not_found => 404 themes/default/public/404.html
155
+ #
156
+ # ### Example `GET /page_not_found` request routed to a 404 Haml template
157
+ #
158
+ # |-- content
159
+ # | `-- index.haml
160
+ # `-- themes
161
+ # `-- default
162
+ # `-- views
163
+ # `-- 404.haml
164
+ #
165
+ # GET /page_not_found => 404 themes/default/views/404.haml
166
+ #
167
+ # If no 404 public file or template is found, the application sends a basic "Page Not Found" page
168
+ # with a 404 HTTP response.
108
169
  #
109
170
  class Application < Sinatra::Base
110
- set :root, Dir.getwd
111
- set :theme, "default"
112
- set :views, lambda { File.join(root, "themes", theme, "views") }
113
- set :public, lambda { File.join(root, "themes", theme, "public") }
114
- set :styles, lambda { File.join(root, "themes", theme, "styles") }
115
- set :content, lambda { File.join(root, "content") }
171
+ # @macro [attach] sinatra.set
172
+ # Defines `settings.$1`
173
+ # @attribute
174
+ # @overload $*
175
+ # @return [String]
176
+ set :root, Dir.getwd
177
+ set :theme, "default"
178
+ set :javascripts, lambda { File.join(root, "themes", theme, "javascripts") }
179
+ set :public, lambda { File.join(root, "themes", theme, "public") }
180
+ set :stylesheets, lambda { File.join(root, "themes", theme, "stylesheets") }
181
+ set :views, lambda { File.join(root, "themes", theme, "views") }
182
+ set :content, lambda { File.join(root, "content") }
183
+
184
+ # @macro [attach] sinatra.mime_type
185
+ # Registers `$2` as the mime type for static files ending with `$1`
186
+ # @scope class
187
+ # @attribute
188
+ # @overload $*
189
+ # @return [String]
190
+ mime_type "", "text/html"
116
191
 
192
+ # @macro [attach] sinatra.configure
193
+ # Run once, at startup, when the environment is set to `${1--1}`
194
+ # @scope class
195
+ # @attribute
196
+ # @overload configure ${1--1}
117
197
  configure :development do
118
198
  use Mango::Rack::Debugger
119
199
  end
120
200
 
121
- # For static files that don't have an extension, send the file as HTML content
122
- #
123
- mime_type "", "text/html"
201
+ # Supported JavaScript template engines
202
+ JAVASCRIPT_TEMPLATE_ENGINES = {
203
+ Tilt::CoffeeScriptTemplate => :coffee
204
+ }
205
+
206
+ # Supported stylesheet template engines
207
+ STYLESHEET_TEMPLATE_ENGINES = {
208
+ Tilt::ScssTemplate => :scss,
209
+ Tilt::SassTemplate => :sass
210
+ }
211
+
212
+ # Supported view template engines
213
+ VIEW_TEMPLATE_ENGINES = {
214
+ Tilt::HamlTemplate => :haml,
215
+ Tilt::ERBTemplate => :erb,
216
+ Tilt::LiquidTemplate => :liquid
217
+ }
218
+
219
+ ###############################################################################################
220
+
221
+ private
124
222
 
125
- # Renders the `404.haml` template found within `settings.views` and sends it with 404 HTTP
126
- # response.
223
+ # Renders a 404 page with a 404 HTTP response code.
127
224
  #
128
- # The `404.haml` template is **not** wrapped within the `layout.haml` template when rendered,
129
- # even if one exists within `settings.views`.
225
+ # First, the application attempts to render a public 404 file stored in `settings.public`. If
226
+ # a public 404 file is found, the application will:
227
+ #
228
+ # * Send the public 404 file with a 404 HTTP response code
229
+ # * Halt execution
230
+ #
231
+ # For example:
232
+ #
233
+ # `-- themes
234
+ # `-- default
235
+ # `-- public
236
+ # `-- 404.html
237
+ #
238
+ # GET /page_not_found => 404 themes/default/public/404.html
239
+ #
240
+ # If no match is found, the application attempts to render a 404 template stored in
241
+ # `settings.views`. If a 404 template is found, the application *will not* render it within a
242
+ # layout template, even if an appropriately named layout template exists within
243
+ # `settings.views`. If a 404 template is found, the application will:
244
+ #
245
+ # * Render the 404 template, without a layout template, as HTML
246
+ # * Send the rendered 404 template with a 404 HTTP response code
247
+ # * Halt execution
130
248
  #
131
249
  # For example:
132
250
  #
@@ -139,16 +257,53 @@ module Mango
139
257
  #
140
258
  # GET /page_not_found => 404 themes/default/views/404.haml
141
259
  #
260
+ # Finally, if no matches are found, the application sends a basic "Page Not Found" page with a
261
+ # 404 HTTP response code.
262
+ #
263
+ # @method not_found
264
+ # @visibility public
265
+ #
142
266
  not_found do
143
- haml :"404", :layout => false
267
+ file_name = "404"
268
+ render_404_public_file! file_name
269
+ render_404_template! file_name
270
+ "<!DOCTYPE html><title>404 Page Not Found</title><h1>404 Page Not Found</h1>"
144
271
  end
145
272
 
146
- # Attempts to render style sheet templates found within `settings.styles`
273
+ # Given a file name, attempts to send an public 404 file, if it exists, and halt
147
274
  #
148
- # First, the application attempts to match the URI path with a public CSS file stored in
149
- # `settings.public`. If a public CSS file is found, the handler will:
275
+ # @param [String] file_name
150
276
  #
151
- # * Send the public CSS file with a 200 HTTP response code
277
+ def render_404_public_file!(file_name)
278
+ four_oh_four_path = File.expand_path("#{file_name}.html", settings.public)
279
+ return unless File.file?(four_oh_four_path)
280
+ send_file four_oh_four_path
281
+ end
282
+
283
+ # Given a template name, and with a prioritized list of template engines, attempts to render a
284
+ # 404 template, if one exists, and halt.
285
+ #
286
+ # @param [String] template_name
287
+ # @see VIEW_TEMPLATE_ENGINES
288
+ #
289
+ def render_404_template!(template_name)
290
+ VIEW_TEMPLATE_ENGINES.each do |engine, extension|
291
+ @preferred_extension = extension.to_s
292
+ find_template(settings.views, template_name, engine) do |file|
293
+ next unless File.file?(file)
294
+ halt send(extension, template_name.to_sym, :layout => false)
295
+ end
296
+ end
297
+ end
298
+
299
+ ###############################################################################################
300
+
301
+ # Attempts to render JavaScript templates found within `settings.javascripts`
302
+ #
303
+ # First, the application attempts to match the URI path with a public JavaScript file stored in
304
+ # `settings.public`. If a public JavaScript file is found, the application will:
305
+ #
306
+ # * Send the public JavaScript file with a 200 HTTP response code
152
307
  # * Halt execution
153
308
  #
154
309
  # For example:
@@ -156,68 +311,135 @@ module Mango
156
311
  # `-- themes
157
312
  # `-- default
158
313
  # `-- public
159
- # `-- styles
160
- # `-- reset.css
314
+ # `-- javascripts
315
+ # `-- jquery.js
161
316
  #
162
- # GET /styles/reset.css => 200 themes/default/public/styles/reset.css
317
+ # GET /javascripts/jquery.js => 200 themes/default/public/javascripts/jquery.js
163
318
  #
164
- # If no match is found, the route handler attempts to match the URI path with a style sheet
165
- # template stored in `settings.styles`. If a style sheet template is found, the handler will:
319
+ # If no match is found, the route handler attempts to match the URI path with a JavaScript
320
+ # template stored in `settings.javascripts`. If a JavaScript template is found, the
321
+ # application will:
166
322
  #
167
- # * Convert the style sheet template from Sass to CSS
168
- # * Send the converted style with a 200 HTTP response code
323
+ # * Render the template as Javascript
324
+ # * Send the rendered template with a 200 HTTP response code
169
325
  # * Halt execution
170
326
  #
171
327
  # For example:
172
328
  #
173
329
  # `-- themes
174
330
  # `-- default
175
- # `-- styles
176
- # `-- screen.sass
331
+ # `-- javascripts
332
+ # `-- bundle.coffee
177
333
  #
178
- # GET /styles/screen.css => 200 themes/default/styles/screen.sass
334
+ # GET /javascripts/bundle.js => 200 themes/default/javascripts/bundle.coffee
179
335
  #
180
- # **It's intended that requests to public CSS files and requests to style sheet templates share
181
- # the `/styles/` prefix.**
336
+ # **It's intended that requests to public JavaScript files and requests to JavaScript templates
337
+ # share the `/javascripts/` prefix.**
182
338
  #
183
339
  # Finally, if no matches are found, the route handler passes execution to the `NOT_FOUND` error
184
340
  # handler.
185
341
  #
186
- get "/styles/*.css" do |uri_path|
187
- render_style_sheet! uri_path
342
+ # @macro [attach] sinatra.get
343
+ # @overload get "$1"
344
+ # @visibility public
345
+ # @method get_js
346
+ #
347
+ get "/javascripts/*.js" do |uri_path|
348
+ render_javascript_template! uri_path
188
349
  not_found
189
350
  end
190
351
 
191
- ###############################################################################################
192
-
193
- private
194
-
195
- # Given a URI path, attempts to render a style sheet, if it exists, and halt
352
+ # Given a URI path, attempts to render a JavaScript template, if it exists, and halt
196
353
  #
197
354
  # @param [String] uri_path
355
+ # @see JAVASCRIPT_TEMPLATE_ENGINES
198
356
  #
199
- def render_style_sheet!(uri_path)
200
- styles_match = File.join(settings.styles, "*")
201
- style_sheet_path = build_style_sheet_path(uri_path)
357
+ def render_javascript_template!(uri_path)
358
+ javascript_match = File.join(settings.javascripts, "*")
359
+ javascript_path = File.expand_path(uri_path, settings.javascripts)
202
360
 
203
- return unless File.fnmatch(styles_match, style_sheet_path)
204
- return unless File.file?(style_sheet_path)
361
+ return unless File.fnmatch(javascript_match, javascript_path)
205
362
 
206
- content_type "text/css"
207
- halt sass(uri_path.to_sym, :views => settings.styles)
363
+ JAVASCRIPT_TEMPLATE_ENGINES.each do |engine, extension|
364
+ @preferred_extension = extension.to_s
365
+ find_template(settings.javascripts, uri_path, engine) do |file|
366
+ next unless File.file?(file)
367
+ halt send(extension, uri_path.to_sym, :views => settings.javascripts)
368
+ end
369
+ end
208
370
  end
209
371
 
210
- # Given a URI path, build a path to a potential style sheet
372
+ ###############################################################################################
373
+
374
+ # Attempts to render stylesheet templates found within `settings.stylesheets`
375
+ #
376
+ # First, the application attempts to match the URI path with a public stylesheet file stored in
377
+ # `settings.public`. If a public stylesheet file is found, the application will:
378
+ #
379
+ # * Send the public stylesheet file with a 200 HTTP response code
380
+ # * Halt execution
381
+ #
382
+ # For example:
383
+ #
384
+ # `-- themes
385
+ # `-- default
386
+ # `-- public
387
+ # `-- stylesheets
388
+ # `-- reset.css
389
+ #
390
+ # GET /stylesheets/reset.css => 200 themes/default/public/stylesheets/reset.css
391
+ #
392
+ # If no match is found, the route handler attempts to match the URI path with a stylesheet
393
+ # template stored in `settings.stylesheets`. If a stylesheet template is found, the
394
+ # application will:
395
+ #
396
+ # * Render the template as CSS
397
+ # * Send the rendered template with a 200 HTTP response code
398
+ # * Halt execution
399
+ #
400
+ # For example:
401
+ #
402
+ # `-- themes
403
+ # `-- default
404
+ # `-- stylesheets
405
+ # `-- screen.scss
406
+ #
407
+ # GET /stylesheets/screen.css => 200 themes/default/stylesheets/screen.scss
408
+ #
409
+ # **It's intended that requests to public stylesheet files and requests to stylesheet templates
410
+ # share the `/stylesheets/` prefix.**
411
+ #
412
+ # Finally, if no matches are found, the route handler passes execution to the `NOT_FOUND` error
413
+ # handler.
414
+ #
415
+ # @method get_css
416
+ #
417
+ get "/stylesheets/*.css" do |uri_path|
418
+ render_stylesheet_template! uri_path
419
+ not_found
420
+ end
421
+
422
+ # Given a URI path, attempts to render a stylesheet template, if it exists, and halt
211
423
  #
212
424
  # @param [String] uri_path
213
- # @param [String] format (defaults to `sass`)
214
- # @return [String] The path to a potential style sheet
425
+ # @see STYLESHEET_TEMPLATE_ENGINES
215
426
  #
216
- def build_style_sheet_path(uri_path, format = "sass")
217
- File.expand_path("#{uri_path}.#{format}", settings.styles)
427
+ def render_stylesheet_template!(uri_path)
428
+ stylesheet_match = File.join(settings.stylesheets, "*")
429
+ stylesheet_path = File.expand_path(uri_path, settings.stylesheets)
430
+
431
+ return unless File.fnmatch(stylesheet_match, stylesheet_path)
432
+
433
+ STYLESHEET_TEMPLATE_ENGINES.each do |engine, extension|
434
+ @preferred_extension = extension.to_s
435
+ find_template(settings.stylesheets, uri_path, engine) do |file|
436
+ next unless File.file?(file)
437
+ halt send(extension, uri_path.to_sym, :views => settings.stylesheets)
438
+ end
439
+ end
218
440
  end
219
441
 
220
- public
442
+ ###############################################################################################
221
443
 
222
444
  # Attempts to render content page templates found within `settings.content`
223
445
  #
@@ -237,52 +459,55 @@ module Mango
237
459
  # GET /hello_word.html => 200 themes/default/public/hello_word.html
238
460
  #
239
461
  # If no match is found, the route handler attempts to match the URI path with a content page
240
- # template stored in `settings.content`. If a content page template is found, the handler will:
462
+ # template stored in `settings.content`. If a content page template is found, the application
463
+ # will:
241
464
  #
242
- # * Read the content page into memory and assign it to the `@content_page` instance variable
465
+ # * Read the content page from disk and render the data in memory
243
466
  # * Render the content page's view template file (see `Mango::ContentPages`)
244
- # * A `RuntimeError` is raised if the view template does not exist within `settings.views`
467
+ # * An exception is raised if a registered engine for the view template file cannot be
468
+ # found or if the view template file cannot be found within `settings.views`.
245
469
  # * Send the rendered page with a 200 HTTP response code and halt execution
246
470
  #
247
- # In addition, if a `layout.haml` template exists within `settings.views`, the page's view
248
- # template is wrapped within this layout when rendered.
471
+ # In addition, if a `layout` template file exists within `settings.views` and that layout
472
+ # template file shares the same file extension as the view template, then the page's view
473
+ # template is wrapped within this layout template when rendered.
249
474
  #
250
- # For example:
475
+ # For example, given the following mango application:
251
476
  #
252
477
  # |-- content
253
- # | `-- index.haml
478
+ # | `-- index.markdown
254
479
  # `-- themes
255
480
  # `-- default
256
481
  # `-- views
257
482
  # |-- layout.haml
258
483
  # `-- page.haml
259
484
  #
260
- # GET /index => 200 content/index.haml +
485
+ # where the `index.markdown` content page's view template file is `page.haml`, then:
486
+ #
487
+ # GET /index => 200 content/index.markdown +
261
488
  # themes/default/views/page.haml +
262
489
  # themes/default/views/layout.haml
263
490
  #
264
491
  # Finally, if no matches are found, the route handler passes execution to the `NOT_FOUND` error
265
492
  # handler.
266
493
  #
494
+ # @method get_all
495
+ #
267
496
  get "/*" do |uri_path|
268
497
  render_index_file! uri_path
269
498
  render_content_page! uri_path
270
499
  not_found
271
500
  end
272
501
 
273
- ###############################################################################################
274
-
275
- private
276
-
277
502
  # Given a URI path, attempts to send an index.html file, if it exists, and halt
278
503
  #
279
504
  # @param [String] uri_path
280
505
  #
281
506
  def render_index_file!(uri_path)
282
- return unless directory_path?(uri_path)
507
+ return unless URI.directory?(uri_path)
283
508
 
284
509
  index_match = File.join(settings.public, "*")
285
- index_file_path = build_index_file_path(uri_path)
510
+ index_file_path = File.expand_path(uri_path + "index.html", settings.public)
286
511
 
287
512
  return unless File.fnmatch(index_match, index_file_path)
288
513
  return unless File.file?(index_file_path)
@@ -290,65 +515,72 @@ module Mango
290
515
  send_file index_file_path
291
516
  end
292
517
 
293
- # Given a URI path, build a path to a potential index.html file
294
- #
295
- # @param [String] uri_path
296
- # @return [String] The path to a potential index.html file
297
- #
298
- def build_index_file_path(uri_path)
299
- uri_path += "index.html"
300
- File.expand_path(uri_path, settings.public)
301
- end
518
+ # Raised when a content page is not found on disk
519
+ class ContentPageNotFound < RuntimeError; end
302
520
 
303
- ###############################################################################################
521
+ # Raised when a registered engine for the content page's view template cannot be found in
522
+ # `VIEW_TEMPLATE_ENGINES`
523
+ class RegisteredEngineNotFound < RuntimeError; end
304
524
 
305
- private
525
+ # Raised when the content page's view template cannot be found on disk
526
+ class ViewTemplateNotFound < RuntimeError; end
306
527
 
307
528
  # Given a URI path, attempts to render a content page, if it exists, and halt
308
529
  #
309
530
  # @param [String] uri_path
310
- # @raise [RuntimeError] Raised when the content page's view template cannot be found
531
+ # @raise [RegisteredEngineNotFound] Raised when a registered engine for the content page's
532
+ # view template cannot be found
533
+ # @raise [ViewTemplateNotFound] Raised when the content page's view template cannot be found
311
534
  #
312
535
  def render_content_page!(uri_path)
536
+ uri_path += "index" if URI.directory?(uri_path)
537
+
313
538
  content_match = File.join(settings.content, "*")
314
- content_page_path = build_content_page_path(uri_path)
539
+ content_page_path = File.expand_path(uri_path, settings.content)
315
540
  return unless File.fnmatch(content_match, content_page_path)
316
541
 
317
542
  begin
318
- @content_page = Mango::ContentPage.find_by_path(content_page_path)
319
- rescue Mango::ContentPage::PageNotFound
543
+ content_page = find_content_page(uri_path)
544
+ rescue ContentPageNotFound
320
545
  return
321
546
  end
322
547
 
548
+ view_template_path = File.expand_path(content_page.view, settings.views)
549
+
550
+ begin
551
+ engine = VIEW_TEMPLATE_ENGINES.fetch(Tilt[content_page.view])
552
+ rescue KeyError
553
+ message = "Cannot find registered engine for view template file -- #{view_template_path}"
554
+ raise RegisteredEngineNotFound, message
555
+ end
556
+
323
557
  begin
324
- halt haml(@content_page.view)
558
+ halt send(engine, content_page.view.to_s.templatize, :locals => { :page => content_page })
325
559
  rescue Errno::ENOENT
326
- view_path = File.expand_path("#{@content_page.view}.haml", settings.views)
327
- raise "Unable to find a view template file -- #{view_path}"
560
+ message = "Cannot find view template file -- #{view_template_path}"
561
+ raise ViewTemplateNotFound, message
328
562
  end
329
563
  end
330
564
 
331
- # Given a URI path, build a path to a potential content page
565
+ # Given a URI path, creates a new `ContentPage` instance by searching for and reading a content
566
+ # file from disk. Content files are searched consecutively until a page with a supported
567
+ # content page template engine is found.
332
568
  #
333
569
  # @param [String] uri_path
334
- # @return [String] The path to a potential content page
335
- #
336
- def build_content_page_path(uri_path)
337
- uri_path += "index" if directory_path?(uri_path)
338
- File.expand_path(uri_path, settings.content)
339
- end
340
-
341
- ###############################################################################################
342
-
343
- private
570
+ # @raise [ContentPageNotFound] Raised when a content page cannot be found for the uri path
571
+ # @return [ContentPage] A new instance is created and returned when found
572
+ # @see ContentPage::TEMPLATE_ENGINES
573
+ #
574
+ def find_content_page(uri_path)
575
+ ContentPage::TEMPLATE_ENGINES.each do |engine, extension|
576
+ @preferred_extension = extension.to_s
577
+ find_template(settings.content, uri_path, engine) do |file|
578
+ next unless File.file?(file)
579
+ return ContentPage.new(:data => File.read(file), :engine => engine)
580
+ end
581
+ end
344
582
 
345
- # Given a URI path, determine whether or no it looks like a directory path
346
- #
347
- # @param [String] uri_path
348
- # @return [Boolean] `true` if the path is empty or has a trailing `/`, otherwise `false`
349
- #
350
- def directory_path?(uri_path)
351
- uri_path.empty? || uri_path[-1] == "/"
583
+ raise ContentPageNotFound, "Cannot find content page for path -- #{uri_path}"
352
584
  end
353
585
  end
354
586
  end