brochure 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
data/LICENSE CHANGED
@@ -1,4 +1,5 @@
1
1
  Copyright (c) 2010 Sam Stephenson
2
+ Copyright (c) 2010 Josh Peek
2
3
 
3
4
  Permission is hereby granted, free of charge, to any person obtaining
4
5
  a copy of this software and associated documentation files (the
data/README.md CHANGED
@@ -24,7 +24,7 @@ Sample application structure:
24
24
  Sample `config.ru`:
25
25
 
26
26
  require "brochure"
27
- run Brochure::Application.new(File.dirname(__FILE__))
27
+ run Brochure.app(File.dirname(__FILE__))
28
28
 
29
29
  URLs are automatically mapped to template names. So `/` will render
30
30
  `app/templates/index.html.erb`, `/signup` will render
@@ -43,10 +43,12 @@ render `app/templates/shared/_header.html.erb` inline.
43
43
 
44
44
  $ gem install brochure
45
45
 
46
- Requires [Tilt](http://github.com/rtomayko/tilt).
46
+ Requires [Hike](http://github.com/sstephenson/hike),
47
+ [Rack](http://rack.rubyforge.org/), and
48
+ [Tilt](http://github.com/rtomayko/tilt).
47
49
 
48
50
  # License
49
51
 
50
- Copyright (c) 2010 Sam Stephenson.
52
+ Copyright (c) 2010 Sam Stephenson and Josh Peek.
51
53
 
52
54
  Released under the MIT license. See `LICENSE` for details.
data/lib/brochure.rb CHANGED
@@ -1,115 +1,30 @@
1
+ require "hike"
2
+ require "rack"
1
3
  require "tilt"
2
4
 
3
5
  module Brochure
4
- class Application
5
- def initialize(root)
6
- @app_root = File.expand_path(root)
7
- @helper_root = File.join(@app_root, "app", "helpers")
8
- @template_root = File.join(@app_root, "app", "templates")
9
- @context_class = Context.for(helpers)
10
- @templates = {}
11
- end
12
-
13
- def helpers
14
- @helpers ||= Dir[File.join(@helper_root, "**", "*.rb")].map do |helper_path|
15
- base_name = helper_path[(@helper_root.length + 1)..-1][/(.*?)\.rb$/, 1]
16
- module_names = base_name.split("/").map { |n| Brochure.camelize(n) }
17
- load helper_path
18
- module_names.inject(Kernel) { |mod, name| mod.const_get(name) }
19
- end
20
- end
21
-
22
- def call(env)
23
- logical_path = env["PATH_INFO"][/[^.]+/]
24
- success render(logical_path)
25
- rescue TemplateNotFound => e
26
- not_found
27
- rescue StandardError => e
28
- error e
29
- end
30
-
31
- def find_template_path(logical_path, options = {})
32
- if options[:partial]
33
- path_parts = logical_path.split("/")
34
- logical_path = (path_parts[0..-2] + ["_" + path_parts[-1]]).join("/")
35
- else
36
- return false if File.basename(logical_path)[/^_/]
37
- end
6
+ VERSION = "0.2.0"
38
7
 
39
- template_path = if File.directory?(File.join(@template_root, logical_path))
40
- File.join(@template_root, logical_path, "index.html.erb")
41
- else
42
- File.join(@template_root, logical_path + ".html.erb")
43
- end
44
-
45
- File.exists?(template_path) && template_path
46
- end
8
+ autoload :Application, "brochure/application"
9
+ autoload :Context, "brochure/context"
10
+ autoload :Failsafe, "brochure/failsafe"
11
+ autoload :TemplateNotFound, "brochure/errors"
47
12
 
48
- def render(logical_path, options = {})
49
- if template_path = find_template_path(logical_path, options)
50
- context = @context_class.new(self)
51
- locals = options[:locals] || {}
52
- template_for(template_path).render(context, locals)
53
- else
54
- raise TemplateNotFound, "no such template '#{logical_path}'"
55
- end
56
- end
57
-
58
- def template_for(template_path)
59
- @templates[template_path] ||= Tilt.new(template_path)
60
- end
61
-
62
- def respond_with(status, body, content_type = "text/html, charset=utf-8")
63
- headers = {
64
- "Content-Type" => content_type,
65
- "Content-Length" => body.length.to_s
66
- }
67
- [status, headers, body]
68
- end
69
-
70
- def success(body)
71
- respond_with 200, body
72
- end
73
-
74
- def not_found
75
- respond_with 404, <<-HTML
76
- <!DOCTYPE html>
77
- <html><head><title>Not Found</title></head>
78
- <body><h1>404 Not Found</h1></body></html>
79
- HTML
80
- end
81
-
82
- def error(exception)
83
- warn ["#{exception.class.name}: #{exception}", *exception.backtrace].join("\n ")
84
- respond_with 500, <<-HTML
85
- <!DOCTYPE html>
86
- <html><head><title>Internal Server Error</title></head>
87
- <body><h1>500 Internal Server Error</h1></body></html>
88
- HTML
89
- end
90
- end
91
-
92
- class Context
93
- include Tilt::CompileSite
94
-
95
- def self.for(helpers)
96
- context = Class.new(self)
97
- context.send(:include, *helpers) if helpers.any?
98
- context
99
- end
100
-
101
- def initialize(application)
102
- @application = application
103
- end
104
-
105
- def render(logical_path, locals = {})
106
- @application.render(logical_path, :partial => true, :locals => locals)
13
+ def self.app(root)
14
+ app = Application.new(root)
15
+ if development?
16
+ app = Rack::ShowExceptions.new(app)
17
+ else
18
+ app = Failsafe.new(app)
107
19
  end
20
+ app
108
21
  end
109
22
 
110
- class TemplateNotFound < StandardError; end
111
-
112
23
  def self.camelize(string)
113
24
  string.gsub(/(^|_)(\w)/) { $2.upcase }
114
25
  end
26
+
27
+ def self.development?
28
+ ENV["RACK_ENV"] == "development"
29
+ end
115
30
  end
@@ -0,0 +1,102 @@
1
+ module Brochure
2
+ class Application
3
+ attr_reader :app_root, :helper_root, :template_root, :asset_root
4
+
5
+ def initialize(root)
6
+ @app_root = File.expand_path(root)
7
+ @helper_root = File.join(@app_root, "app", "helpers")
8
+ @template_root = File.join(@app_root, "app", "templates")
9
+ @asset_root = File.join(@app_root, "public")
10
+
11
+ @template_trail = Hike::Trail.new(@app_root)
12
+ @template_trail.extensions.replace(Tilt.mappings.keys.sort)
13
+ @template_trail.paths.push(@template_root)
14
+
15
+ @context_class = Context.for(helpers)
16
+ @templates = {}
17
+ end
18
+
19
+ def helpers
20
+ @helpers ||= Dir[File.join(@helper_root, "**", "*.rb")].map do |helper_path|
21
+ base_name = helper_path[(@helper_root.length + 1)..-1][/(.*?)\.rb$/, 1]
22
+ module_names = base_name.split("/").map { |n| Brochure.camelize(n) }
23
+ load helper_path
24
+ module_names.inject(Kernel) { |mod, name| mod.const_get(name) }
25
+ end
26
+ end
27
+
28
+ def call(env)
29
+ if forbidden?(env["PATH_INFO"])
30
+ forbidden
31
+ elsif template = find_template(env["PATH_INFO"][/[^.]+/])
32
+ success render_template(template, env)
33
+ else
34
+ not_found
35
+ end
36
+ end
37
+
38
+ def forbidden?(path)
39
+ path[".."] || File.basename(path)[/^_/]
40
+ end
41
+
42
+ def find_template(logical_path)
43
+ if template_path = find_template_path(logical_path)
44
+ template_for(template_path)
45
+ end
46
+ end
47
+
48
+ def find_partial(logical_path)
49
+ if template_path = find_partial_path(logical_path)
50
+ template_for(template_path)
51
+ end
52
+ end
53
+
54
+ def find_template_path(logical_path)
55
+ candidates = [logical_path + ".html", logical_path + "/index.html"]
56
+ @template_trail.find(*candidates)
57
+ end
58
+
59
+ def find_partial_path(logical_path)
60
+ path_parts = logical_path.split("/")
61
+ partial_path = (path_parts[0..-2] + ["_" + path_parts[-1]]).join("/")
62
+ @template_trail.find(partial_path + ".html")
63
+ end
64
+
65
+ def template_for(template_path)
66
+ if Brochure.development?
67
+ Tilt.new(template_path)
68
+ else
69
+ @templates[template_path] ||= Tilt.new(template_path)
70
+ end
71
+ end
72
+
73
+ def render_template(template, env, locals = {})
74
+ context = @context_class.new(self, env)
75
+ template.render(context, locals)
76
+ end
77
+
78
+ def respond_with(status, body, content_type = "text/html, charset=utf-8")
79
+ headers = {
80
+ "Content-Type" => content_type,
81
+ "Content-Length" => body.length.to_s
82
+ }
83
+ [status, headers, [body]]
84
+ end
85
+
86
+ def success(body)
87
+ respond_with 200, body
88
+ end
89
+
90
+ def not_found
91
+ respond_with 404, <<-HTML
92
+ <!DOCTYPE html>
93
+ <html><head><title>Not Found</title></head>
94
+ <body><h1>404 Not Found</h1></body></html>
95
+ HTML
96
+ end
97
+
98
+ def forbidden
99
+ respond_with 403, "Forbidden"
100
+ end
101
+ end
102
+ end
@@ -0,0 +1,30 @@
1
+ module Brochure
2
+ class Context
3
+ include Tilt::CompileSite
4
+
5
+ def self.for(helpers)
6
+ context = Class.new(self)
7
+ context.send(:include, *helpers) if helpers.any?
8
+ context
9
+ end
10
+
11
+ attr_accessor :application, :env
12
+
13
+ def initialize(application, env)
14
+ self.application = application
15
+ self.env = env
16
+ end
17
+
18
+ def request
19
+ @_request ||= Rack::Request.new(env)
20
+ end
21
+
22
+ def render(logical_path, locals = {})
23
+ if template = @application.find_partial(logical_path)
24
+ @application.render_template(template, env, locals)
25
+ else
26
+ raise TemplateNotFound, "no such template '#{logical_path}'"
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,3 @@
1
+ module Brochure
2
+ class TemplateNotFound < StandardError; end
3
+ end
@@ -0,0 +1,26 @@
1
+ module Brochure
2
+ class Failsafe
3
+ def initialize(app)
4
+ @app = app
5
+ end
6
+
7
+ def call(env)
8
+ @app.call(env)
9
+ rescue Exception => exception
10
+ backtrace = ["#{exception.class.name}: #{exception}", *exception.backtrace].join("\n ")
11
+ env["rack.errors"].puts(backtrace)
12
+ env["rack.errors"].flush
13
+
14
+ body = <<-HTML
15
+ <!DOCTYPE html>
16
+ <html><head><title>Internal Server Error</title></head>
17
+ <body><h1>500 Internal Server Error</h1></body></html>
18
+ HTML
19
+
20
+ [500,
21
+ { "Content-Type" => "text/html, charset=utf-8",
22
+ "Content-Length" => Rack::Utils.bytesize(body).to_s },
23
+ [body]]
24
+ end
25
+ end
26
+ end
metadata CHANGED
@@ -1,41 +1,76 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: brochure
3
3
  version: !ruby/object:Gem::Version
4
- hash: 27
4
+ hash: 23
5
5
  prerelease: false
6
6
  segments:
7
7
  - 0
8
- - 1
8
+ - 2
9
9
  - 0
10
- version: 0.1.0
10
+ version: 0.2.0
11
11
  platform: ruby
12
12
  authors:
13
13
  - Sam Stephenson
14
+ - Josh Peek
14
15
  autorequire:
15
16
  bindir: bin
16
17
  cert_chain: []
17
18
 
18
- date: 2010-10-12 00:00:00 -05:00
19
+ date: 2010-10-16 00:00:00 -05:00
19
20
  default_executable:
20
21
  dependencies:
21
22
  - !ruby/object:Gem::Dependency
22
- name: tilt
23
+ name: hike
23
24
  prerelease: false
24
25
  requirement: &id001 !ruby/object:Gem::Requirement
25
26
  none: false
26
27
  requirements:
27
28
  - - ">="
28
29
  - !ruby/object:Gem::Version
29
- hash: 3
30
+ hash: 25
30
31
  segments:
31
32
  - 0
32
- version: "0"
33
+ - 1
34
+ - 1
35
+ version: 0.1.1
33
36
  type: :runtime
34
37
  version_requirements: *id001
35
38
  - !ruby/object:Gem::Dependency
36
- name: rack-test
39
+ name: rack
37
40
  prerelease: false
38
41
  requirement: &id002 !ruby/object:Gem::Requirement
42
+ none: false
43
+ requirements:
44
+ - - ">="
45
+ - !ruby/object:Gem::Version
46
+ hash: 23
47
+ segments:
48
+ - 1
49
+ - 0
50
+ - 0
51
+ version: 1.0.0
52
+ type: :runtime
53
+ version_requirements: *id002
54
+ - !ruby/object:Gem::Dependency
55
+ name: tilt
56
+ prerelease: false
57
+ requirement: &id003 !ruby/object:Gem::Requirement
58
+ none: false
59
+ requirements:
60
+ - - ">="
61
+ - !ruby/object:Gem::Version
62
+ hash: 19
63
+ segments:
64
+ - 1
65
+ - 1
66
+ - 0
67
+ version: 1.1.0
68
+ type: :runtime
69
+ version_requirements: *id003
70
+ - !ruby/object:Gem::Dependency
71
+ name: rack-test
72
+ prerelease: false
73
+ requirement: &id004 !ruby/object:Gem::Requirement
39
74
  none: false
40
75
  requirements:
41
76
  - - ">="
@@ -45,10 +80,11 @@ dependencies:
45
80
  - 0
46
81
  version: "0"
47
82
  type: :development
48
- version_requirements: *id002
83
+ version_requirements: *id004
49
84
  description: A Rack application for serving static sites with ERB templates.
50
85
  email:
51
86
  - sstephenson@gmail.com
87
+ - josh@joshpeek.com
52
88
  executables: []
53
89
 
54
90
  extensions: []
@@ -56,6 +92,10 @@ extensions: []
56
92
  extra_rdoc_files: []
57
93
 
58
94
  files:
95
+ - lib/brochure/application.rb
96
+ - lib/brochure/context.rb
97
+ - lib/brochure/errors.rb
98
+ - lib/brochure/failsafe.rb
59
99
  - lib/brochure.rb
60
100
  - README.md
61
101
  - LICENSE