frank 0.2.6 → 0.3.0.beta

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 (64) hide show
  1. data/Featurelist +6 -0
  2. data/README.md +44 -32
  3. data/Rakefile +3 -3
  4. data/frank.gemspec +51 -45
  5. data/lib/frank.rb +1 -0
  6. data/lib/frank/base.rb +144 -94
  7. data/lib/frank/lorem.rb +77 -36
  8. data/lib/frank/middleware/imager.rb +43 -0
  9. data/lib/frank/middleware/refresh.rb +42 -0
  10. data/lib/frank/middleware/statik.rb +41 -0
  11. data/lib/frank/output.rb +25 -37
  12. data/lib/frank/rescue.rb +13 -6
  13. data/lib/frank/template_helpers.rb +34 -3
  14. data/lib/frank/templates/404.haml +1 -0
  15. data/lib/frank/templates/500.haml +2 -0
  16. data/lib/frank/tilt.rb +389 -141
  17. data/lib/template/{dynamic/layout.haml → layouts/default.haml} +0 -0
  18. data/lib/template/settings.yml +15 -28
  19. data/spec/base_spec.rb +88 -0
  20. data/{test → spec}/helper.rb +0 -7
  21. data/spec/output_spec.rb +220 -0
  22. data/spec/render_spec.rb +106 -0
  23. data/spec/template/dynamic/500.haml +1 -0
  24. data/{test → spec}/template/dynamic/_partial.haml +0 -0
  25. data/{test → spec}/template/dynamic/builder.builder +0 -0
  26. data/{test → spec}/template/dynamic/coffee.coffee +0 -0
  27. data/{test → spec}/template/dynamic/erb.erb +0 -0
  28. data/{test → spec}/template/dynamic/helper_test.haml +0 -0
  29. data/spec/template/dynamic/index.haml +5 -0
  30. data/spec/template/dynamic/layout2_test.haml +4 -0
  31. data/{test → spec}/template/dynamic/liquid.liquid +0 -0
  32. data/spec/template/dynamic/lorem_test.haml +7 -0
  33. data/{test → spec}/template/dynamic/markdown.md +0 -0
  34. data/spec/template/dynamic/markdown_in_haml.md +4 -0
  35. data/{test → spec}/template/dynamic/mustache.mustache +0 -0
  36. data/spec/template/dynamic/nested/child.haml +1 -0
  37. data/spec/template/dynamic/nested/deeper/deep.haml +1 -0
  38. data/spec/template/dynamic/no_layout.haml +4 -0
  39. data/spec/template/dynamic/partial_test.haml +2 -0
  40. data/{test → spec}/template/dynamic/redcloth.textile +0 -0
  41. data/spec/template/dynamic/refresh.haml +1 -0
  42. data/{test → spec}/template/dynamic/sass.sass +0 -0
  43. data/{test → spec}/template/helpers.rb +0 -0
  44. data/spec/template/layouts/default.haml +3 -0
  45. data/{test/template/dynamic → spec/template/layouts/explicit}/layout2.haml +0 -0
  46. data/spec/template/layouts/nested/default.haml +2 -0
  47. data/spec/template/settings.yml +45 -0
  48. data/{test/template/static → spec/template/static/files}/static.html +0 -0
  49. data/spec/template_helpers_spec.rb +78 -0
  50. metadata +57 -49
  51. data/lib/frank/imager.rb +0 -39
  52. data/lib/frank/statik.rb +0 -39
  53. data/test/suite.rb +0 -4
  54. data/test/template/dynamic/index.haml +0 -2
  55. data/test/template/dynamic/layout.haml +0 -2
  56. data/test/template/dynamic/layout2_test.haml +0 -1
  57. data/test/template/dynamic/layout_test.haml +0 -1
  58. data/test/template/dynamic/lorem_test.haml +0 -7
  59. data/test/template/dynamic/partial_test.haml +0 -2
  60. data/test/template/settings.yml +0 -62
  61. data/test/test_base.rb +0 -81
  62. data/test/test_helpers.rb +0 -71
  63. data/test/test_output.rb +0 -160
  64. data/test/test_render.rb +0 -89
@@ -0,0 +1,6 @@
1
+ - DONE - support layouts of a different type than the rendered template
2
+ dynamically reload template changes using javascript and a slick url
3
+ dynamically reload helpers and settings
4
+ - DONE - support yaml variable definition at the top of templates
5
+ - DONE - add helper for "selected" css class, this should work with frankup and frankout
6
+ - DONE - add frankout replacement fields to lorem helpers
data/README.md CHANGED
@@ -25,22 +25,21 @@ Then `cd <project_name>` and start up the server with:
25
25
  Frank's holdin' it down...
26
26
  0.0.0.0:3601
27
27
 
28
- And you're ready to get to work. By default, dynamic templates are served from the `dynamic` folder
29
- and static files are served from the `static` folder.
28
+ And you're ready to get to work. By default, dynamic templates are served from the `dynamic` folder,
29
+ static files are served from the `static` folder, and layouts are server from the `layouts` folder.
30
30
 
31
31
  When you are finished:
32
-
33
- $ frankout <dump_dir> # compile templates
32
+ `$ frankout <dump_dir> # compile templates`
34
33
 
35
34
  or
36
35
 
37
- $ frankout --production <dump_dir> # compile and create folder structure suitable for serving from a production website
36
+ `$ frankout --production <dump_dir> # compile and create folder structure suitable for serving from a production website`
38
37
 
39
- Views & Layouts
38
+ Views & Meta Data
40
39
  -------------------------
41
40
 
42
41
  All of your templates, and less/sass/coffeescript go into `<project>/dynamic`,
43
- by default. You are more than welcome to organize them into subfolders if you've
42
+ by default. You can organize them into subfolders if you've
44
43
  got lots.
45
44
 
46
45
  ### Views
@@ -48,21 +47,36 @@ got lots.
48
47
  Writing views is simple. Say you've got a `blog.haml`, in `<project>/dynamic` just browse to
49
48
  `http://0.0.0.0:3601/blog` and your view will be parsed and returned as html.
50
49
 
51
- ### Layouts
50
+ ### Meta Data
51
+
52
+ Frank doesn't have controllers and there are times you need to pass variables around between templates and layouts.
53
+ This can be done with template Meta data. Meta data is set using YAML.
54
+
55
+ You define your fields at the top a template
56
+ a separate it from the rest of your template using the Meta delimiter: `META--------`. The delimiter can contain as
57
+ many dashes, or hyphens as you wish.
58
+
59
+ You can access your fields as local variables in the template (if the template language supports it).
60
+ For example, you might have a template and define a field `title: My Rad Template`, then inside a haml layout,
61
+ you could create a title tag with the field: `%title= title`
52
62
 
53
- Layouts are also simple with Frank. By default, just create a `layout.haml`
54
- (or whichever language you like best), that contains a `yield`, and any
63
+ Layouts (updated in 0.3)
64
+ -----------------------------
65
+
66
+ Layouts are also simple with Frank. By default, just create a `default.haml`
67
+ (or another language), inside the `layouts` folder and include a `yield` statement. Any
55
68
  views will be inserted into it at that point.
56
69
 
57
- Multiple layouts are also easy. In your `settings.yml`, do something like:
70
+ Layouts can be name spaced with folders:
71
+
72
+ a template: `dynamic_folder/blog/a-blog-post.haml`
73
+ would look for a layout: `layouts/blog/default.haml`
74
+ and if not found use the default: `layouts/default.haml`
75
+
76
+ Frank also supports defining layouts on an individual template basis using meta data
77
+ you can do this by defining a meta field `layout: my_layout.haml` You can disable layouts on a
78
+ template by using `layout: nil`
58
79
 
59
- layouts:
60
- - name: blog_layout
61
- only: [blog]
62
- - name: normal
63
- not: [blog, ajax]
64
- This tells Frank to use `blog_layout.haml` for `/blog`, and `normal.haml`
65
- for everything but `/blog' and '/ajax`.
66
80
 
67
81
 
68
82
  Partials & Helpers
@@ -93,7 +107,13 @@ to the `FrankHelpers` module; that's it. Use them just like `render_partial`.
93
107
  Built-in Helpers
94
108
  ----------------
95
109
 
96
- Frank also comes with some handy helper methods for generating placeholder content.
110
+ ### Auto Refresh
111
+
112
+ Constantly refreshing your browser can become tedious while doing work. Frank has a handy refresh helper.
113
+ It will include a bit of javascript that refreshes the browser when you save the current template or it's layout.
114
+ You can include this in a haml template like this: `= refresh`. When you `frankout`,
115
+ the template will render an empty string instead of the script tag
116
+
97
117
 
98
118
  ### Placeholder Text
99
119
 
@@ -113,7 +133,7 @@ This will return 3 sentences of standard [Lorem Ipsum][11]. `lorem` also has all
113
133
  lorem.first_name
114
134
  lorem.last_name
115
135
  lorem.email
116
-
136
+
117
137
 
118
138
  ### Placeholder Images
119
139
 
@@ -129,19 +149,11 @@ The `lorem.image` helper returns a special Frank image URL. In this case, the re
129
149
 
130
150
  If you would like to use the placeholder images in a context where the helper methods are unavailable (e.g. in static CSS or JavaScript), you can access the URL directly with `/_img/500x400.jpg`, or for random images `/_img/500x400.jpg?random`.
131
151
 
152
+ ### Replacement Text
132
153
 
133
-
134
- GET/POST params
135
- ---------------
136
-
137
- Sometimes it's nice to include user input in your mock-ups. It's especially
138
- handy when mocking-up Ajax-driven elements. For this reason, the `request`
139
- and `params` are available in your templates.
140
-
141
- For example, to use a person's name submitted through a form you might do:
142
-
143
- %h1= "Hello, #{params.name}"
144
-
154
+ All of the lorem helpers accept an optional "replacement" argument. This will be the text rendered when you `frankout`.
155
+ For example `lorem.sentence("<%= page.content %>")` will generate a lorem sentence when you view the page using the `frankup` server.
156
+ However, when you `frankout` the template will render "<%= page.content %>".
145
157
 
146
158
 
147
159
  Configuration
data/Rakefile CHANGED
@@ -4,8 +4,8 @@ begin
4
4
  require 'jeweler'
5
5
  Jeweler::Tasks.new do |gemspec|
6
6
  gemspec.name = "frank"
7
- gemspec.summary = "Stupidly Simple Static Slinger"
8
- gemspec.description = "Create/Dump static builds using whatever templating/helper languages you wish"
7
+ gemspec.summary = "Static Site Non-Framework"
8
+ gemspec.description = "Rapidly develop static sites using any supported templating language"
9
9
  gemspec.version = Frank::VERSION
10
10
  gemspec.email = "travis.dunn@thisismedium.com"
11
11
  gemspec.homepage = "http://github.com/blahed/frank"
@@ -13,7 +13,7 @@ begin
13
13
  gemspec.add_dependency 'rack', '>=1.0'
14
14
  gemspec.add_dependency 'mongrel', '>=1.0'
15
15
  gemspec.add_dependency 'haml', '>=2.0'
16
- gemspec.add_development_dependency 'shoulda', '>=2.0'
16
+ gemspec.add_development_dependency 'rspec'
17
17
  gemspec.add_development_dependency 'rack-test', '>=0.5'
18
18
 
19
19
  end
@@ -1,16 +1,16 @@
1
1
  # Generated by jeweler
2
2
  # DO NOT EDIT THIS FILE DIRECTLY
3
- # Instead, edit Jeweler::Tasks in Rakefile, and run the gemspec command
3
+ # Instead, edit Jeweler::Tasks in rakefile, and run the gemspec command
4
4
  # -*- encoding: utf-8 -*-
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = %q{frank}
8
- s.version = "0.2.6"
8
+ s.version = "0.3.0.beta"
9
9
 
10
- s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
10
+ s.required_rubygems_version = Gem::Requirement.new("> 1.3.1") if s.respond_to? :required_rubygems_version=
11
11
  s.authors = ["blahed", "nwah"]
12
- s.date = %q{2010-04-14}
13
- s.description = %q{Create/Dump static builds using whatever templating/helper languages you wish}
12
+ s.date = %q{2010-06-12}
13
+ s.description = %q{Rapidly develop static sites using any supported templating language}
14
14
  s.email = %q{travis.dunn@thisismedium.com}
15
15
  s.executables = ["frank", "frankout", "frankup"]
16
16
  s.extra_rdoc_files = [
@@ -19,6 +19,7 @@ Gem::Specification.new do |s|
19
19
  ]
20
20
  s.files = [
21
21
  ".gitignore",
22
+ "Featurelist",
22
23
  "LICENSE",
23
24
  "README.md",
24
25
  "Rakefile",
@@ -28,11 +29,12 @@ Gem::Specification.new do |s|
28
29
  "frank.gemspec",
29
30
  "lib/frank.rb",
30
31
  "lib/frank/base.rb",
31
- "lib/frank/imager.rb",
32
32
  "lib/frank/lorem.rb",
33
+ "lib/frank/middleware/imager.rb",
34
+ "lib/frank/middleware/refresh.rb",
35
+ "lib/frank/middleware/statik.rb",
33
36
  "lib/frank/output.rb",
34
37
  "lib/frank/rescue.rb",
35
- "lib/frank/statik.rb",
36
38
  "lib/frank/template_helpers.rb",
37
39
  "lib/frank/templates/404.haml",
38
40
  "lib/frank/templates/500.haml",
@@ -51,51 +53,55 @@ Gem::Specification.new do |s|
51
53
  "lib/frank/tilt.rb",
52
54
  "lib/template/dynamic/css/frank.sass",
53
55
  "lib/template/dynamic/index.haml",
54
- "lib/template/dynamic/layout.haml",
55
56
  "lib/template/helpers.rb",
57
+ "lib/template/layouts/default.haml",
56
58
  "lib/template/settings.yml",
57
59
  "lib/template/static/images/frank-med.png",
58
60
  "lib/template/static/js/frank.js",
59
- "test/helper.rb",
60
- "test/suite.rb",
61
- "test/template/dynamic/_partial.haml",
62
- "test/template/dynamic/builder.builder",
63
- "test/template/dynamic/coffee.coffee",
64
- "test/template/dynamic/erb.erb",
65
- "test/template/dynamic/helper_test.haml",
66
- "test/template/dynamic/index.haml",
67
- "test/template/dynamic/layout.haml",
68
- "test/template/dynamic/layout2.haml",
69
- "test/template/dynamic/layout2_test.haml",
70
- "test/template/dynamic/layout_test.haml",
71
- "test/template/dynamic/liquid.liquid",
72
- "test/template/dynamic/lorem_test.haml",
73
- "test/template/dynamic/markdown.md",
74
- "test/template/dynamic/mustache.mustache",
75
- "test/template/dynamic/partial_test.haml",
76
- "test/template/dynamic/redcloth.textile",
77
- "test/template/dynamic/sass.sass",
78
- "test/template/helpers.rb",
79
- "test/template/settings.yml",
80
- "test/template/static/static.html",
81
- "test/test_base.rb",
82
- "test/test_helpers.rb",
83
- "test/test_output.rb",
84
- "test/test_render.rb"
61
+ "spec/base_spec.rb",
62
+ "spec/helper.rb",
63
+ "spec/output_spec.rb",
64
+ "spec/render_spec.rb",
65
+ "spec/template/dynamic/500.haml",
66
+ "spec/template/dynamic/_partial.haml",
67
+ "spec/template/dynamic/builder.builder",
68
+ "spec/template/dynamic/coffee.coffee",
69
+ "spec/template/dynamic/erb.erb",
70
+ "spec/template/dynamic/helper_test.haml",
71
+ "spec/template/dynamic/index.haml",
72
+ "spec/template/dynamic/layout2_test.haml",
73
+ "spec/template/dynamic/liquid.liquid",
74
+ "spec/template/dynamic/lorem_test.haml",
75
+ "spec/template/dynamic/markdown.md",
76
+ "spec/template/dynamic/markdown_in_haml.md",
77
+ "spec/template/dynamic/mustache.mustache",
78
+ "spec/template/dynamic/nested/child.haml",
79
+ "spec/template/dynamic/nested/deeper/deep.haml",
80
+ "spec/template/dynamic/no_layout.haml",
81
+ "spec/template/dynamic/partial_test.haml",
82
+ "spec/template/dynamic/redcloth.textile",
83
+ "spec/template/dynamic/refresh.haml",
84
+ "spec/template/dynamic/sass.sass",
85
+ "spec/template/helpers.rb",
86
+ "spec/template/layouts/default.haml",
87
+ "spec/template/layouts/explicit/layout2.haml",
88
+ "spec/template/layouts/nested/default.haml",
89
+ "spec/template/settings.yml",
90
+ "spec/template/static/files/static.html",
91
+ "spec/template_helpers_spec.rb"
85
92
  ]
86
93
  s.homepage = %q{http://github.com/blahed/frank}
87
94
  s.rdoc_options = ["--charset=UTF-8"]
88
95
  s.require_paths = ["lib"]
89
96
  s.rubygems_version = %q{1.3.6}
90
- s.summary = %q{Stupidly Simple Static Slinger}
97
+ s.summary = %q{Static Site Non-Framework}
91
98
  s.test_files = [
92
- "test/helper.rb",
93
- "test/suite.rb",
94
- "test/template/helpers.rb",
95
- "test/test_base.rb",
96
- "test/test_helpers.rb",
97
- "test/test_output.rb",
98
- "test/test_render.rb"
99
+ "spec/base_spec.rb",
100
+ "spec/helper.rb",
101
+ "spec/output_spec.rb",
102
+ "spec/render_spec.rb",
103
+ "spec/template/helpers.rb",
104
+ "spec/template_helpers_spec.rb"
99
105
  ]
100
106
 
101
107
  if s.respond_to? :specification_version then
@@ -106,20 +112,20 @@ Gem::Specification.new do |s|
106
112
  s.add_runtime_dependency(%q<rack>, [">= 1.0"])
107
113
  s.add_runtime_dependency(%q<mongrel>, [">= 1.0"])
108
114
  s.add_runtime_dependency(%q<haml>, [">= 2.0"])
109
- s.add_development_dependency(%q<shoulda>, [">= 2.0"])
115
+ s.add_development_dependency(%q<rspec>, [">= 0"])
110
116
  s.add_development_dependency(%q<rack-test>, [">= 0.5"])
111
117
  else
112
118
  s.add_dependency(%q<rack>, [">= 1.0"])
113
119
  s.add_dependency(%q<mongrel>, [">= 1.0"])
114
120
  s.add_dependency(%q<haml>, [">= 2.0"])
115
- s.add_dependency(%q<shoulda>, [">= 2.0"])
121
+ s.add_dependency(%q<rspec>, [">= 0"])
116
122
  s.add_dependency(%q<rack-test>, [">= 0.5"])
117
123
  end
118
124
  else
119
125
  s.add_dependency(%q<rack>, [">= 1.0"])
120
126
  s.add_dependency(%q<mongrel>, [">= 1.0"])
121
127
  s.add_dependency(%q<haml>, [">= 2.0"])
122
- s.add_dependency(%q<shoulda>, [">= 2.0"])
128
+ s.add_dependency(%q<rspec>, [">= 0"])
123
129
  s.add_dependency(%q<rack-test>, [">= 0.5"])
124
130
  end
125
131
  end
@@ -11,6 +11,7 @@ end
11
11
 
12
12
  require 'rubygems'
13
13
  require 'yaml'
14
+ require 'fileutils'
14
15
  require 'rack'
15
16
  require 'frank/base'
16
17
  require 'frank/output'
@@ -1,11 +1,12 @@
1
1
  require 'frank/tilt'
2
2
  require 'frank/template_helpers'
3
3
  require 'frank/rescue'
4
- require 'frank/statik'
5
- require 'frank/imager'
4
+ require 'frank/middleware/statik'
5
+ require 'frank/middleware/imager'
6
+ require 'frank/middleware/refresh'
6
7
 
7
8
  module Frank
8
- VERSION = '0.2.6'
9
+ VERSION = '0.3.0.beta'
9
10
 
10
11
  module Render; end
11
12
 
@@ -15,7 +16,13 @@ module Frank
15
16
  include Frank::TemplateHelpers
16
17
  include Frank::Render
17
18
 
18
- attr_accessor :environment, :proj_dir, :server, :static_folder, :dynamic_folder, :templates
19
+ attr_accessor :environment
20
+ attr_accessor :proj_dir
21
+ attr_accessor :server
22
+ attr_accessor :static_folder
23
+ attr_accessor :dynamic_folder
24
+ attr_accessor :layouts_folder
25
+ attr_accessor :templates
19
26
 
20
27
  def initialize(&block)
21
28
  instance_eval &block
@@ -46,9 +53,9 @@ module Frank
46
53
  # attempt to render with the request path,
47
54
  # if it cannot be found, render error page
48
55
  def process
49
- ext = File.extname(@request.path.split('/').last || '')
50
- @response['Content-Type'] = Rack::Mime.mime_type(ext, 'text/html')
51
- @response.write render_path(@request.path)
56
+ load_helpers
57
+ @response['Content-Type'] = Rack::Mime.mime_type(File.extname(@request.path), 'text/html')
58
+ @response.write render(@request.path)
52
59
  rescue Frank::TemplateError
53
60
  render_404
54
61
  rescue Exception => e
@@ -57,115 +64,158 @@ module Frank
57
64
 
58
65
  # prints requests and errors to STDOUT
59
66
  def log_request(status, excp=nil)
60
- out = "[#{Time.now.strftime('%Y-%m-%d %H:%M')}] (#{@request.request_method}) http://#{@request.host}:#{@request.port}#{@request.fullpath} - #{status}"
61
- out += "\n\n**QUACK** #{excp.message}\n\n#{excp.backtrace.join("\n")} " if excp
62
- STDOUT.puts out unless @environment == :test
67
+ out = "\033[1m[#{Time.now.strftime('%Y-%m-%d %H:%M')}]\033[22m (#{@request.request_method}) http://#{@request.host}:#{@request.port}#{@request.fullpath} - #{status}"
68
+ out << "\n\n#{excp.message}\n\n#{excp.backtrace.join("\n")} " if excp
69
+ puts out
70
+ end
71
+
72
+ def load_helpers
73
+ helpers = File.join(@proj_dir, 'helpers.rb')
74
+ if File.exist? helpers
75
+ load helpers
76
+ Frank::TemplateHelpers.class_eval("include FrankHelpers")
77
+ end
63
78
  end
64
79
 
65
80
  end
66
81
 
67
82
  module Render
68
83
 
69
- def name_ext(path)
70
- return path.split(/\.(?=[^.]+$)/)
71
- end
84
+ TMPL_EXTS = {
85
+ :html => %w[haml erb rhtml builder liquid mustache textile md mkd markdown],
86
+ :css => %w[sass less],
87
+ :js => %w[coffee]
88
+ }
72
89
 
73
- # breaks down path and renders partials, js, css without layouts
74
- def render_path(path)
75
- path.sub!(/^\//,'')
76
- template, ext = find_template_ext(path)
90
+ # render request path or template path
91
+ def render(path)
92
+ # normalize the path
93
+ path.sub!(/^\/?(.*)$/, '/\1')
94
+ path.sub!(/\/$/, '/index.html')
95
+ path.sub!(/(\/[\w-]+)$/, '\1.html')
96
+ path = to_file_path(path) if defined? @request
97
+
98
+ # regex for kinds that don't support meta
99
+ # and define the meta delimiter
100
+ nometa, delimiter = /\/_|\.(js|coffee|css|sass|less)$/, /^META-{3,}\n$/
77
101
 
78
- raise Frank::TemplateError, "Template not found #{path}" if template.nil?
79
- if template.match(/^_/) or (ext||'').match(/^(js|css)$/)
80
- render_template template
102
+ # set the layout
103
+ layout = path.match(nometa) ? nil : layout_for(path)
104
+
105
+ template_path = File.join(@proj_dir, @dynamic_folder, path)
106
+ raise Frank::TemplateError, "Template not found #{template_path}" unless File.exist? template_path
107
+
108
+ # read in the template
109
+ # check for meta and parse it if it exists
110
+ template = File.read(template_path) << "\n"
111
+ ext = File.extname(path)
112
+ template, meta = template.split(delimiter).reverse
113
+ locals = parse_meta_and_set_locals(meta, path)
114
+
115
+ # use given layout if defined as a meta field
116
+ layout = locals[:layout] == 'nil' ? nil : locals[:layout] if locals.has_key?(:layout)
117
+
118
+ # let tilt determine the template handler
119
+ # and return some template markup
120
+ if layout.nil?
121
+ tilt(ext, template, locals)
81
122
  else
82
- render_with_layout template
123
+ layout_path = File.join(@proj_dir, @layouts_folder, layout)
124
+ # add layout_path to locals
125
+ raise Frank::TemplateError, "Layout not found #{layout_path}" unless File.exist? layout_path
126
+
127
+ tilt(File.extname(layout), layout_path, locals) do
128
+ tilt(ext, template, locals)
129
+ end
83
130
  end
84
131
  end
85
132
 
86
- # renders a template
87
- def render_template(tmpl, *args)
88
- tilt(File.join(@proj_dir, @dynamic_folder, tmpl), *args) {"CONTENT"}
89
- end
90
-
91
- # if template has a layout defined, render template within layout
92
- # otherwise render template
93
- def render_with_layout(tmpl, *args)
94
- if layout = get_layout_for(tmpl)
95
- tilt(File.join(@proj_dir, @dynamic_folder, layout), *args) do
96
- render_template tmpl
133
+ # converts a request path to a template path
134
+ def to_file_path(path)
135
+ file_name = File.basename(path, File.extname(path))
136
+ file_ext = File.extname(path).sub(/^\./, '')
137
+ folder = File.join(@proj_dir, @dynamic_folder)
138
+ engine = nil
139
+
140
+ TMPL_EXTS.each do |ext, engines|
141
+ if ext.to_s == file_ext
142
+ engine = engines.reject do |eng|
143
+ !File.exist? File.join(folder, path.sub(/\.[\w-]+$/, ".#{eng}"))
144
+ end.first
97
145
  end
98
- else
99
- render_template tmpl
100
146
  end
147
+
148
+ raise Frank::TemplateError, "Template not found #{path}" if engine.nil?
149
+
150
+ path.sub(/\.[\w-]+$/, ".#{engine}")
101
151
  end
102
152
 
103
- TMPL_EXTS = { :html => %w[haml erb rhtml builder liquid mustache textile md mkd markdown],
104
- :css => %w[sass less],
105
- :js => %w[coffee] }
106
-
107
- def reverse_ext_lookup(ext)
108
- TMPL_EXTS.each do |kind, exts|
109
- return kind.to_s if exts.index(ext)
153
+ # lookup the original ext for given template path
154
+ # TODO: make non-ugly
155
+ def ext_from_handler(extension)
156
+ orig_ext = nil
157
+ TMPL_EXTS.each do |ext, engines|
158
+ orig_ext = ext.to_s if engines.include? extension[1..-1]
110
159
  end
111
- nil
160
+ orig_ext
112
161
  end
162
+
113
163
 
114
- # finds template extension based on filename
115
- # TODO: cleanup
116
- def find_template_ext(filename)
117
- name, kind = name_ext(filename)
118
- kind = reverse_ext_lookup(kind) if kind && TMPL_EXTS[kind.intern].nil?
164
+ # reverse walks the layouts folder until we find a layout
165
+ # returns nil if layout is not found
166
+ def layout_for(path)
167
+ default = "default#{File.extname(path)}"
168
+ path = path.sub /\/[\w-]+\.[\w-]+$/, ''
169
+ folders = path.split('/')
119
170
 
120
- tmpl_ext = nil
121
-
122
- TMPL_EXTS[ kind.nil? ? :html : kind.intern ].each do |ext|
123
- tmpl = "#{(name||'')}.#{ext}"
124
- default = File.join((name||''), "index.#{ext}")
125
-
126
- if File.exists? File.join(@proj_dir, @dynamic_folder, tmpl)
127
- tmpl_ext = [tmpl, ext]
128
- elsif File.exists? File.join(@proj_dir, @dynamic_folder, default)
129
- tmpl_ext = [default, ext]
171
+ until File.exist? File.join(@proj_dir, @layouts_folder, folders, default)
172
+ break if folders.empty?
173
+ folders.pop
174
+ end
175
+
176
+ if File.exist? File.join(@proj_dir, @layouts_folder, folders, default)
177
+ File.join(folders, default)
178
+ else
179
+ nil
180
+ end
181
+ end
182
+
183
+ # setup an object and extend it with TemplateHelpers and Render
184
+ # then send everything to tilt and get some template markup back
185
+ def tilt(ext, source, locals={}, &block)
186
+ obj = Object.new.extend(TemplateHelpers).extend(Render)
187
+ instance_variables.each do |var|
188
+ unless ['@response', '@env'].include? var
189
+ obj.instance_variable_set(var.intern, instance_variable_get(var))
130
190
  end
131
191
  end
132
-
133
- tmpl_ext
192
+ Tilt[ext].new(source).render(obj, locals=locals, &block)
134
193
  end
135
194
 
136
- # determines layout using layouts settings
137
- # TODO: cleanup
138
- def get_layout_for(view)
139
- view, ext = name_ext(view)
195
+ private
196
+
197
+ # parse the given meta string with yaml
198
+ # add current path
199
+ # and add instance variables
200
+ def parse_meta_and_set_locals(meta, path)
201
+ locals = {}
140
202
 
141
- layouts = @templates['layouts'] || []
142
- onlies = layouts.select {|l| l['only'] }
143
- nots = layouts.select {|l| l['not'] }
144
- blanks = layouts - onlies - nots
145
-
146
- layout = onlies.select {|l| l['only'].index(view) }.first
147
- layout = nots.reject {|l| l['not'].index(view) }.first unless layout
148
- layout = blanks.first unless layout
203
+ # parse yaml and symbolize keys
204
+ if meta.nil?
205
+ meta = {}
206
+ else
207
+ meta = YAML.load(meta).inject({}) do |options, (key, value)|
208
+ options[(key.to_sym rescue key) || key] = value
209
+ options
210
+ end
211
+ end
149
212
 
150
- # TODO: we are checking for exts in two places, consolidate soon
151
- layout = nil if !blanks.empty? && blanks.first['name'] == view
152
- layout = nil if (TMPL_EXTS[:css] + TMPL_EXTS[:js]).include?(ext)
153
-
154
- layout.nil? ? nil : layout['name'] + '.' + ext
155
- end
156
-
157
- # TODO: cleanup
158
- def tilt(file, *args, &block)
159
- locals = @request.nil? ? {} : { :request => @env, :params => @request.params }
160
- obj = Object.new.extend(TemplateHelpers).extend(Render)
161
- obj.instance_variable_set(:@proj_dir, @proj_dir)
162
- obj.instance_variable_set(:@dynamic_folder, @dynamic_folder)
163
- obj.instance_variable_set(:@templates, @templates)
164
- Tilt.new(file, 1).render(obj, locals, &block)
165
- end
166
-
167
- def remove_ext(path)
168
- path.gsub(File.extname(path), '')
213
+ # normalize current_path
214
+ # and add it to locals
215
+ current_path = path.sub(/\.[\w-]+$/, '').sub(/\/index/, '/')
216
+ locals[:current_path] = current_path
217
+
218
+ meta.merge(locals)
169
219
  end
170
220
 
171
221
  end
@@ -175,8 +225,9 @@ module Frank
175
225
  base = Base.new(&block) if block_given?
176
226
 
177
227
  builder = Rack::Builder.new do
178
- use Frank::Statik, :root => base.static_folder
179
- use Frank::Imager
228
+ use Frank::Middleware::Statik, :root => base.static_folder
229
+ use Frank::Middleware::Imager
230
+ use Frank::Middleware::Refresh, :watch => [ base.dynamic_folder, base.static_folder, base.layouts_folder ]
180
231
  run base
181
232
  end
182
233
 
@@ -201,8 +252,7 @@ module Frank
201
252
 
202
253
  # copies over the generic project template
203
254
  def self.stub(project)
204
- puts "\nFrank is..."
205
- puts " - \033[32mCreating\033[0m your project '#{project}'"
255
+ puts "\nFrank is...\n - \033[32mCreating\033[0m your project '#{project}'"
206
256
  Dir.mkdir project
207
257
  puts " - \033[32mCopying\033[0m Frank template"
208
258
  FileUtils.cp_r( Dir.glob(File.join(LIBDIR, 'template/*')), project )