renee-render 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/README.md ADDED
@@ -0,0 +1,38 @@
1
+ # Renee Render
2
+
3
+ Rendering templates in Renee should be familiar and intuitive using the `render` command:
4
+
5
+ ```ruby
6
+ run Renee::Core.new {
7
+ path('blog') do
8
+ get { render! :haml, :"blogs/index" }
9
+ end
10
+ }
11
+ ```
12
+
13
+ This above is the standard render syntax, specifying the engine followed by the template. You can also render without specifying an engine:
14
+
15
+ ```ruby
16
+ path('blog') do
17
+ get { render! "blogs/index" }
18
+ end
19
+ ```
20
+
21
+ This will do a lookup in the views path to find the appropriately named template. You can also pass locals and layout options as you would expect:
22
+
23
+ ```ruby
24
+ path('blog') do
25
+ get { render! "blogs/index", :locals => { :foo => "bar" }, :layout => :bar }
26
+ end
27
+ ```
28
+
29
+ This will render the "blogs/index.erb" file if it exists, passing the 'foo' local variable
30
+ and wrapping the result in the 'bar.erb' layout file. You can also render without returning the response by using:
31
+
32
+ ```ruby
33
+ path('blog') do
34
+ get { render "blogs/index" }
35
+ end
36
+ ```
37
+
38
+ This allows you to render the content as a string without immediately responding.
data/Rakefile ADDED
@@ -0,0 +1,8 @@
1
+ require "bundler/gem_tasks"
2
+ require 'rake/testtask'
3
+
4
+ Rake::TestTask.new do |t|
5
+ t.libs.push "lib"
6
+ t.test_files = FileList[File.expand_path('../test/**/*_test.rb', __FILE__)]
7
+ t.verbose = true
8
+ end
@@ -0,0 +1,6 @@
1
+ class Renee
2
+ module Render
3
+ # The version for the renee-render gem.
4
+ VERSION = "0.0.1"
5
+ end
6
+ end
@@ -0,0 +1,136 @@
1
+ require 'tilt'
2
+
3
+ class Renee
4
+ # This module is responsible for handling the rendering of templates
5
+ # using Tilt supporting all included template engines.
6
+ module Render
7
+ ##
8
+ # Exception responsible for when a generic rendering error occurs.
9
+ #
10
+ class RenderError < RuntimeError; end
11
+
12
+ ##
13
+ # Exception responsible for when an expected template does not exist.
14
+ #
15
+ class TemplateNotFound < RenderError; end
16
+
17
+ # Same as render but automatically halts.
18
+ # @param (see #render)
19
+ # @return (see #render)
20
+ # @see #render
21
+ def render!(*args, &blk)
22
+ halt render(*args, &blk)
23
+ end
24
+
25
+ ##
26
+ # Renders a string given the engine and the content.
27
+ #
28
+ # @param [Symbol] engine The template engine to use for rendering.
29
+ # @param [String] data The content or file to render.
30
+ # @param [Hash] options The rendering options to pass onto tilt.
31
+ #
32
+ # @return [String] The result of rendering the data with specified engine.
33
+ #
34
+ # @example
35
+ # render :haml, "%p test" => "<p>test</p>"
36
+ # render :haml, :index => "<p>test</p>"
37
+ # render "index" => "<p>test</p>"
38
+ #
39
+ # @api public
40
+ #
41
+ def render(engine, data=nil, options={}, &block)
42
+ # Handles the case where engine is unspecified by shifting the data (i.e render "index")
43
+ engine, data, options = nil, engine.to_sym, data if data.nil? || data.is_a?(Hash)
44
+
45
+ options ||= {}
46
+ options[:outvar] ||= '@_out_buf'
47
+ # TODO allow default encoding to be set (as an option)
48
+ options[:default_encoding] ||= "utf-8"
49
+
50
+ locals = options.delete(:locals) || {}
51
+ views = options.delete(:views) || settings.views_path || "./views"
52
+ layout = options.delete(:layout)
53
+ layout_engine = options.delete(:layout_engine) || engine
54
+ # TODO suppress template errors for layouts?
55
+ # TODO allow content_type to be set with an option to render?
56
+ scope = options.delete(:scope) || self
57
+
58
+ # TODO default layout file convention?
59
+ template = compile_template(engine, data, options, views)
60
+ output = template.render(scope, locals, &block)
61
+
62
+ if layout # render layout
63
+ # TODO handle when layout is missing better!
64
+ options = options.merge(:views => views, :layout => false, :scope => scope)
65
+ return render(layout_engine, layout, options.merge(:locals => locals)) { output }
66
+ end
67
+
68
+ output
69
+ end # render
70
+
71
+ ##
72
+ # Constructs a template based on engine, data and options.
73
+ #
74
+ # @param [Symbol] engine The template engine to use for rendering.
75
+ # @param [String] data The content or file to render.
76
+ # @param [Hash] options The rendering options to pass onto tilt.
77
+ # @param [String] views The view_path from which to locate the template.
78
+ #
79
+ # @return [Tilt::Template] The tilt template to render with all required options.
80
+ # @raise [TemplateNotFound] The template to render could not be located.
81
+ # @raise [RenderError] The template to render could not be located.
82
+ #
83
+ # @api private
84
+ #
85
+ def compile_template(engine, data, options, views)
86
+ template_cache.fetch engine, data, options do
87
+ if data.is_a?(Symbol) # data is template path
88
+ file_path, engine = find_template(views, data, engine)
89
+ template = Tilt[engine]
90
+ raise TemplateNotFound, "Template engine not found: #{engine}" if template.nil?
91
+ raise TemplateNotFound, "Template '#{data}' not found in '#{views}'!" unless file_path
92
+ # TODO suppress errors for layouts?
93
+ template.new(file_path, 1, options)
94
+ elsif data.is_a?(String) # data is body string
95
+ # TODO figure out path based on caller file
96
+ path, line = options[:path] || "caller file", options[:line] || 1
97
+ body = data.is_a?(String) ? Proc.new { data } : data
98
+ template = Tilt[engine]
99
+ raise "Template engine not found: #{engine}" if template.nil?
100
+ template.new(path, line.to_i, options, &body)
101
+ else # data can't be handled
102
+ raise RenderError, "Cannot render data #{data.inspect}."
103
+ end
104
+ end # template_cache.fetch
105
+ end # compile_template
106
+
107
+ ##
108
+ # Searches view paths for template based on data and engine with rendering options.
109
+ # Supports finding a template without an engine.
110
+ #
111
+ # @param [String] views The view paths
112
+ # @param [String] name The name of the template
113
+ # @param [Symbol] engine The engine to use for rendering.
114
+ #
115
+ # @return [<String, Symbol>] An array of the file path and the engine.
116
+ #
117
+ # @example
118
+ # find_template("./views", "index", :erb) => ["path/to/index.erb", :erb]
119
+ # find_template("./views", "foo") => ["path/to/index.haml", :haml]
120
+ #
121
+ # @api private
122
+ #
123
+ def find_template(views, name, engine=nil)
124
+ lookup_ext = (engine || File.extname(name.to_s)[1..-1] || "*").to_s
125
+ base_name = name.to_s.chomp(".#{lookup_ext}")
126
+ file_path = Dir[File.join(views, "#{base_name}.#{lookup_ext}")].first
127
+ engine ||= File.extname(file_path)[1..-1].to_sym if file_path
128
+ [file_path, engine]
129
+ end # find_template
130
+
131
+ # Maintain Tilt::Cache of the templates.
132
+ def template_cache
133
+ @template_cache ||= Tilt::Cache.new
134
+ end
135
+ end
136
+ end
@@ -0,0 +1,28 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "renee-render/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "renee-render"
7
+ s.version = Renee::Render::VERSION
8
+ s.authors = ["Josh Hull", "Nathan Esquenazi", "Arthur Chiu"]
9
+ s.email = ["joshbuddy@gmail.com", "nesquena@gmail.com", "mr.arthur.chiu@gmail.com"]
10
+ s.homepage = ""
11
+ s.summary = %q{The super-friendly web framework rendering component}
12
+ s.description = %q{The super-friendly web framework rendering component.}
13
+
14
+ s.rubyforge_project = "renee"
15
+
16
+ s.files = `git ls-files`.split("\n")
17
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
18
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
19
+ s.require_paths = ["lib"]
20
+
21
+ s.add_runtime_dependency 'rack', "~> 1.3.0"
22
+ s.add_runtime_dependency 'tilt', "~> 1.3.3"
23
+ s.add_development_dependency 'minitest', "~> 2.6.1"
24
+ s.add_development_dependency 'rake'
25
+ s.add_development_dependency 'bundler', "~> 1.0.10"
26
+ s.add_development_dependency "rack-test", ">= 0.5.0"
27
+ s.add_development_dependency "haml", ">= 2.2.0"
28
+ end
@@ -0,0 +1,114 @@
1
+ require File.expand_path('../test_helper', __FILE__)
2
+
3
+ describe Renee::Render do
4
+ describe "#render" do
5
+ after { remove_views }
6
+
7
+ it "should allow rendering string with engine" do
8
+ mock_app {
9
+ path("/a") { get { render! :erb, "<p>test</p>" } }
10
+ path("/b") { get { render! :erb, "<p><%= foo %></p>", :locals => { :foo => "bar" } } }
11
+ path("/c") { get { halt render(:erb, "<p><%= foo %></p>", :locals => { :foo => "bar" }) } }
12
+ }
13
+ get('/a')
14
+ assert_equal 200, response.status
15
+ assert_equal "<p>test</p>", response.body
16
+ get('/b')
17
+ assert_equal 200, response.status
18
+ assert_equal "<p>bar</p>", response.body
19
+ get('/c')
20
+ assert_equal 200, response.status
21
+ assert_equal "<p>bar</p>", response.body
22
+ end # string, with engine
23
+
24
+ it "should allow rendering template file with engine" do
25
+ create_view :index, "%p test", :haml
26
+ create_view :foo, "%p= foo", :haml
27
+ mock_app {
28
+ path("/a") { get { render! :haml, :index } }
29
+ path("/b") { get { render! :haml, :foo, :locals => { :foo => "bar" } } }
30
+ path("/c") { get { halt render(:haml, :foo, :locals => { :foo => "bar" }) } }
31
+ }
32
+ get('/a')
33
+ assert_equal 200, response.status
34
+ assert_equal "<p>test</p>\n", response.body
35
+ get('/b')
36
+ assert_equal 200, response.status
37
+ assert_equal "<p>bar</p>\n", response.body
38
+ get('/c')
39
+ assert_equal 200, response.status
40
+ assert_equal "<p>bar</p>\n", response.body
41
+ end # template, with engine
42
+
43
+ it "should allow rendering template file with unspecified engine" do
44
+ create_view :index, "%p test", :haml
45
+ create_view :foo, "%p= foo", :haml
46
+ mock_app {
47
+ path("/a") { get { render! "index" } }
48
+ path("/b") { get { render! "foo.haml", :locals => { :foo => "bar" } } }
49
+ }
50
+ get('/a')
51
+ assert_equal 200, response.status
52
+ assert_equal "<p>test</p>\n", response.body
53
+ get('/b')
54
+ assert_equal 200, response.status
55
+ assert_equal "<p>bar</p>\n", response.body
56
+ end # template, unspecified engine
57
+
58
+ it "should allow rendering template file with engine and layout" do
59
+ create_view :index, "%p test", :haml
60
+ create_view :foo, "%p= foo", :haml
61
+ create_view :layout, "%div.wrapper= yield", :haml
62
+ mock_app {
63
+ path("/a") { get { render! :haml, :index, :layout => :layout } }
64
+ path("/b") { get { render! :foo, :layout => :layout, :locals => { :foo => "bar" } } }
65
+ }
66
+ get('/a')
67
+ assert_equal 200, response.status
68
+ assert_equal %Q{<div class='wrapper'><p>test</p></div>\n}, response.body
69
+ get('/b')
70
+ assert_equal 200, response.status
71
+ assert_equal %Q{<div class='wrapper'><p>bar</p></div>\n}, response.body
72
+ end # with engine and layout specified
73
+
74
+ it "should allow rendering template with different layout engines" do
75
+ create_view :index, "%p test", :haml
76
+ create_view :foo, "%p= foo", :haml
77
+ create_view :base, "<div class='wrapper'><%= yield %></div>", :erb
78
+ mock_app {
79
+ path("/a") { get { render! :haml, :index, :layout => :base, :layout_engine => :erb } }
80
+ path("/b") { get { render! :foo, :layout => :base, :locals => { :foo => "bar" } } }
81
+ }
82
+ get('/a')
83
+ assert_equal 200, response.status
84
+ assert_equal %Q{<div class='wrapper'><p>test</p>\n</div>}, response.body
85
+ get('/b')
86
+ assert_equal 200, response.status
87
+ assert_equal %Q{<div class='wrapper'><p>bar</p>\n</div>}, response.body
88
+ end # different layout and template engines
89
+
90
+ it "should fail properly rendering template file with invalid engine" do
91
+ create_view :index, "%p test", :haml
92
+ mock_app {
93
+ get { render! :fake, :index }
94
+ }
95
+ assert_raises(Renee::Render::TemplateNotFound) { get('/') }
96
+ end # template, invalid engine
97
+
98
+ it "should fail properly rendering missing template file with engine" do
99
+ create_view :index, "%p test", :haml
100
+ mock_app {
101
+ get { render! :haml, :foo }
102
+ }
103
+ assert_raises(Renee::Render::TemplateNotFound) { get('/') }
104
+ end # missing template, with engine
105
+
106
+ it "should fail properly rendering invalid data" do
107
+ create_view :index, "%p test", :haml
108
+ mock_app {
109
+ get { render! :haml, /invalid regex data/ }
110
+ }
111
+ assert_raises(Renee::Render::RenderError) { get('/') }
112
+ end # missing template, with engine
113
+ end
114
+ end
@@ -0,0 +1,10 @@
1
+ $: << File.expand_path('../../../renee-core/lib', __FILE__)
2
+ $: << File.expand_path('../../lib', __FILE__)
3
+ require 'renee-core'
4
+ require 'renee-render'
5
+
6
+ # TODO better registration method (?)
7
+ Renee::Core::Application.send(:include, Renee::Render)
8
+
9
+ # Load shared test helpers
10
+ require File.expand_path('../../../lib/test_helper', __FILE__)
metadata ADDED
@@ -0,0 +1,186 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: renee-render
3
+ version: !ruby/object:Gem::Version
4
+ hash: 29
5
+ prerelease:
6
+ segments:
7
+ - 0
8
+ - 0
9
+ - 1
10
+ version: 0.0.1
11
+ platform: ruby
12
+ authors:
13
+ - Josh Hull
14
+ - Nathan Esquenazi
15
+ - Arthur Chiu
16
+ autorequire:
17
+ bindir: bin
18
+ cert_chain: []
19
+
20
+ date: 2011-10-15 00:00:00 Z
21
+ dependencies:
22
+ - !ruby/object:Gem::Dependency
23
+ version_requirements: &id001 !ruby/object:Gem::Requirement
24
+ none: false
25
+ requirements:
26
+ - - ~>
27
+ - !ruby/object:Gem::Version
28
+ hash: 27
29
+ segments:
30
+ - 1
31
+ - 3
32
+ - 0
33
+ version: 1.3.0
34
+ requirement: *id001
35
+ type: :runtime
36
+ prerelease: false
37
+ name: rack
38
+ - !ruby/object:Gem::Dependency
39
+ version_requirements: &id002 !ruby/object:Gem::Requirement
40
+ none: false
41
+ requirements:
42
+ - - ~>
43
+ - !ruby/object:Gem::Version
44
+ hash: 29
45
+ segments:
46
+ - 1
47
+ - 3
48
+ - 3
49
+ version: 1.3.3
50
+ requirement: *id002
51
+ type: :runtime
52
+ prerelease: false
53
+ name: tilt
54
+ - !ruby/object:Gem::Dependency
55
+ version_requirements: &id003 !ruby/object:Gem::Requirement
56
+ none: false
57
+ requirements:
58
+ - - ~>
59
+ - !ruby/object:Gem::Version
60
+ hash: 21
61
+ segments:
62
+ - 2
63
+ - 6
64
+ - 1
65
+ version: 2.6.1
66
+ requirement: *id003
67
+ type: :development
68
+ prerelease: false
69
+ name: minitest
70
+ - !ruby/object:Gem::Dependency
71
+ version_requirements: &id004 !ruby/object:Gem::Requirement
72
+ none: false
73
+ requirements:
74
+ - - ">="
75
+ - !ruby/object:Gem::Version
76
+ hash: 3
77
+ segments:
78
+ - 0
79
+ version: "0"
80
+ requirement: *id004
81
+ type: :development
82
+ prerelease: false
83
+ name: rake
84
+ - !ruby/object:Gem::Dependency
85
+ version_requirements: &id005 !ruby/object:Gem::Requirement
86
+ none: false
87
+ requirements:
88
+ - - ~>
89
+ - !ruby/object:Gem::Version
90
+ hash: 3
91
+ segments:
92
+ - 1
93
+ - 0
94
+ - 10
95
+ version: 1.0.10
96
+ requirement: *id005
97
+ type: :development
98
+ prerelease: false
99
+ name: bundler
100
+ - !ruby/object:Gem::Dependency
101
+ version_requirements: &id006 !ruby/object:Gem::Requirement
102
+ none: false
103
+ requirements:
104
+ - - ">="
105
+ - !ruby/object:Gem::Version
106
+ hash: 11
107
+ segments:
108
+ - 0
109
+ - 5
110
+ - 0
111
+ version: 0.5.0
112
+ requirement: *id006
113
+ type: :development
114
+ prerelease: false
115
+ name: rack-test
116
+ - !ruby/object:Gem::Dependency
117
+ version_requirements: &id007 !ruby/object:Gem::Requirement
118
+ none: false
119
+ requirements:
120
+ - - ">="
121
+ - !ruby/object:Gem::Version
122
+ hash: 7
123
+ segments:
124
+ - 2
125
+ - 2
126
+ - 0
127
+ version: 2.2.0
128
+ requirement: *id007
129
+ type: :development
130
+ prerelease: false
131
+ name: haml
132
+ description: The super-friendly web framework rendering component.
133
+ email:
134
+ - joshbuddy@gmail.com
135
+ - nesquena@gmail.com
136
+ - mr.arthur.chiu@gmail.com
137
+ executables: []
138
+
139
+ extensions: []
140
+
141
+ extra_rdoc_files: []
142
+
143
+ files:
144
+ - README.md
145
+ - Rakefile
146
+ - lib/renee-render.rb
147
+ - lib/renee-render/version.rb
148
+ - renee-render.gemspec
149
+ - test/render_test.rb
150
+ - test/test_helper.rb
151
+ homepage: ""
152
+ licenses: []
153
+
154
+ post_install_message:
155
+ rdoc_options: []
156
+
157
+ require_paths:
158
+ - lib
159
+ required_ruby_version: !ruby/object:Gem::Requirement
160
+ none: false
161
+ requirements:
162
+ - - ">="
163
+ - !ruby/object:Gem::Version
164
+ hash: 3
165
+ segments:
166
+ - 0
167
+ version: "0"
168
+ required_rubygems_version: !ruby/object:Gem::Requirement
169
+ none: false
170
+ requirements:
171
+ - - ">="
172
+ - !ruby/object:Gem::Version
173
+ hash: 3
174
+ segments:
175
+ - 0
176
+ version: "0"
177
+ requirements: []
178
+
179
+ rubyforge_project: renee
180
+ rubygems_version: 1.8.10
181
+ signing_key:
182
+ specification_version: 3
183
+ summary: The super-friendly web framework rendering component
184
+ test_files:
185
+ - test/render_test.rb
186
+ - test/test_helper.rb