brochure 0.1.0 → 0.2.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.
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