renee 0.3.11 → 0.4.0.pre1
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/Gemfile +17 -0
- data/Gemfile-renee +8 -0
- data/Gemfile-renee-core +8 -0
- data/Gemfile-renee-render +9 -0
- data/Gemfile-renee-session +9 -0
- data/Gemfile-renee-url-generation +8 -0
- data/MIT-LICENSE.txt +7 -0
- data/README-renee-core.md +242 -0
- data/README-renee-render.md +38 -0
- data/README-renee-session.md +3 -0
- data/README-renee-url-generation.md +3 -0
- data/README.md +131 -6
- data/Rakefile +109 -9
- data/TODO.txt +45 -0
- data/config.ru +26 -0
- data/examples/blog/blog.rb +3 -1
- data/examples/blog/config.ru +24 -19
- data/examples/blog/views/edit.erb +10 -1
- data/examples/blog/views/show.erb +5 -0
- data/lib/renee.rb +11 -4
- data/lib/renee/core.rb +98 -0
- data/lib/renee/core/chaining.rb +66 -0
- data/lib/renee/core/env_accessors.rb +72 -0
- data/lib/renee/core/exceptions.rb +15 -0
- data/lib/renee/core/matcher.rb +61 -0
- data/lib/renee/core/plugins.rb +31 -0
- data/lib/renee/core/rack_interaction.rb +50 -0
- data/lib/renee/core/request_context.rb +56 -0
- data/lib/renee/core/responding.rb +112 -0
- data/lib/renee/core/response.rb +78 -0
- data/lib/renee/core/routing.rb +319 -0
- data/lib/renee/core/transform.rb +18 -0
- data/lib/renee/render.rb +221 -0
- data/lib/renee/session.rb +50 -0
- data/lib/renee/url_generation.rb +117 -0
- data/lib/renee/util.rb +7 -0
- data/lib/renee/version.rb +2 -4
- data/plan.txt +19 -0
- data/renee-core.gemspec +26 -0
- data/renee-render.gemspec +30 -0
- data/renee-session.gemspec +28 -0
- data/renee-url-generation.gemspec +24 -0
- data/renee.gemspec +5 -6
- data/site/MIT-LICENSE.txt +7 -0
- data/site/public/css/app.css +75 -0
- data/site/public/docs/renee-core/Renee.html +208 -0
- data/site/public/docs/renee-core/Renee/Core.html +366 -0
- data/site/public/docs/renee-core/Renee/Core/Chaining.html +192 -0
- data/site/public/docs/renee-core/Renee/Core/ClassMethods.html +725 -0
- data/site/public/docs/renee-core/Renee/Core/ClientError.html +317 -0
- data/site/public/docs/renee-core/Renee/Core/EnvAccessors.html +152 -0
- data/site/public/docs/renee-core/Renee/Core/EnvAccessors/ClassMethods.html +354 -0
- data/site/public/docs/renee-core/Renee/Core/Matcher.html +675 -0
- data/site/public/docs/renee-core/Renee/Core/Plugins.html +475 -0
- data/site/public/docs/renee-core/Renee/Core/RackInteraction.html +488 -0
- data/site/public/docs/renee-core/Renee/Core/RequestContext.html +511 -0
- data/site/public/docs/renee-core/Renee/Core/Responding.html +877 -0
- data/site/public/docs/renee-core/Renee/Core/Response.html +691 -0
- data/site/public/docs/renee-core/Renee/Core/Routing.html +1589 -0
- data/site/public/docs/renee-core/Renee/Core/Transform.html +249 -0
- data/site/public/docs/renee-core/Renee/Core/URLGeneration.html +597 -0
- data/site/public/docs/renee-core/_index.html +244 -0
- data/site/public/docs/renee-core/class_list.html +47 -0
- data/site/public/docs/renee-core/css/common.css +1 -0
- data/site/public/docs/renee-core/css/full_list.css +55 -0
- data/site/public/docs/renee-core/css/style.css +322 -0
- data/site/public/docs/renee-core/file.README-renee-core.html +341 -0
- data/site/public/docs/renee-core/file.README.html +212 -0
- data/site/public/docs/renee-core/file_list.html +49 -0
- data/site/public/docs/renee-core/frames.html +13 -0
- data/site/public/docs/renee-core/index.html +341 -0
- data/site/public/docs/renee-core/js/app.js +205 -0
- data/site/public/docs/renee-core/js/full_list.js +167 -0
- data/site/public/docs/renee-core/js/jquery.js +16 -0
- data/site/public/docs/renee-core/method_list.html +590 -0
- data/site/public/docs/renee-core/top-level-namespace.html +103 -0
- data/site/public/docs/renee-render/Renee.html +116 -0
- data/site/public/docs/renee-render/Renee/Core.html +346 -0
- data/site/public/docs/renee-render/Renee/Core/Chaining.html +125 -0
- data/site/public/docs/renee-render/Renee/Core/ClassMethods.html +620 -0
- data/site/public/docs/renee-render/Renee/Core/ClientError.html +317 -0
- data/site/public/docs/renee-render/Renee/Core/EnvAccessors.html +152 -0
- data/site/public/docs/renee-render/Renee/Core/EnvAccessors/ClassMethods.html +354 -0
- data/site/public/docs/renee-render/Renee/Core/Matcher.html +675 -0
- data/site/public/docs/renee-render/Renee/Core/RackInteraction.html +488 -0
- data/site/public/docs/renee-render/Renee/Core/RequestContext.html +421 -0
- data/site/public/docs/renee-render/Renee/Core/Responding.html +873 -0
- data/site/public/docs/renee-render/Renee/Core/Response.html +691 -0
- data/site/public/docs/renee-render/Renee/Core/Routing.html +1682 -0
- data/site/public/docs/renee-render/Renee/Core/Transform.html +249 -0
- data/site/public/docs/renee-render/Renee/Core/URLGeneration.html +597 -0
- data/site/public/docs/renee-render/Renee/Render.html +873 -0
- data/site/public/docs/renee-render/Renee/Render/ClassMethods.html +382 -0
- data/site/public/docs/renee-render/Renee/Render/TemplateNotFound.html +126 -0
- data/site/public/docs/renee-render/_index.html +143 -0
- data/site/public/docs/renee-render/class_list.html +47 -0
- data/site/public/docs/renee-render/css/common.css +1 -0
- data/site/public/docs/renee-render/css/full_list.css +55 -0
- data/site/public/docs/renee-render/css/style.css +322 -0
- data/site/public/docs/renee-render/file.README-renee-render.html +104 -0
- data/site/public/docs/renee-render/file.README.html +212 -0
- data/site/public/docs/renee-render/file_list.html +49 -0
- data/site/public/docs/renee-render/frames.html +13 -0
- data/site/public/docs/renee-render/index.html +104 -0
- data/site/public/docs/renee-render/js/app.js +205 -0
- data/site/public/docs/renee-render/js/full_list.js +167 -0
- data/site/public/docs/renee-render/js/jquery.js +16 -0
- data/site/public/docs/renee-render/method_list.html +110 -0
- data/site/public/docs/renee-render/top-level-namespace.html +103 -0
- data/site/public/docs/renee-session/Renee.html +106 -0
- data/site/public/docs/renee-session/Renee/Session.html +173 -0
- data/site/public/docs/renee-session/Renee/Session/ClassMethods.html +470 -0
- data/site/public/docs/renee-session/_index.html +136 -0
- data/site/public/docs/renee-session/class_list.html +47 -0
- data/site/public/docs/renee-session/css/common.css +1 -0
- data/site/public/docs/renee-session/css/full_list.css +55 -0
- data/site/public/docs/renee-session/css/style.css +322 -0
- data/site/public/docs/renee-session/file.README-renee-core.html +341 -0
- data/site/public/docs/renee-session/file.README-renee-session.html +69 -0
- data/site/public/docs/renee-session/file_list.html +49 -0
- data/site/public/docs/renee-session/frames.html +13 -0
- data/site/public/docs/renee-session/index.html +69 -0
- data/site/public/docs/renee-session/js/app.js +205 -0
- data/site/public/docs/renee-session/js/full_list.js +167 -0
- data/site/public/docs/renee-session/js/jquery.js +16 -0
- data/site/public/docs/renee-session/method_list.html +102 -0
- data/site/public/docs/renee-session/top-level-namespace.html +103 -0
- data/site/public/docs/renee-url-generation/Renee.html +208 -0
- data/site/public/docs/renee-url-generation/Renee/Core.html +366 -0
- data/site/public/docs/renee-url-generation/Renee/Core/Chaining.html +192 -0
- data/site/public/docs/renee-url-generation/Renee/Core/ClassMethods.html +725 -0
- data/site/public/docs/renee-url-generation/Renee/Core/ClientError.html +317 -0
- data/site/public/docs/renee-url-generation/Renee/Core/EnvAccessors.html +152 -0
- data/site/public/docs/renee-url-generation/Renee/Core/EnvAccessors/ClassMethods.html +354 -0
- data/site/public/docs/renee-url-generation/Renee/Core/Matcher.html +675 -0
- data/site/public/docs/renee-url-generation/Renee/Core/Plugins.html +475 -0
- data/site/public/docs/renee-url-generation/Renee/Core/RackInteraction.html +488 -0
- data/site/public/docs/renee-url-generation/Renee/Core/RequestContext.html +511 -0
- data/site/public/docs/renee-url-generation/Renee/Core/Responding.html +877 -0
- data/site/public/docs/renee-url-generation/Renee/Core/Response.html +691 -0
- data/site/public/docs/renee-url-generation/Renee/Core/Routing.html +1589 -0
- data/site/public/docs/renee-url-generation/Renee/Core/Transform.html +249 -0
- data/site/public/docs/renee-url-generation/_index.html +244 -0
- data/site/public/docs/renee-url-generation/class_list.html +47 -0
- data/site/public/docs/renee-url-generation/css/common.css +1 -0
- data/site/public/docs/renee-url-generation/css/full_list.css +55 -0
- data/site/public/docs/renee-url-generation/css/style.css +322 -0
- data/site/public/docs/renee-url-generation/file.README-renee-url-generation.html +69 -0
- data/site/public/docs/renee-url-generation/file_list.html +49 -0
- data/site/public/docs/renee-url-generation/frames.html +13 -0
- data/site/public/docs/renee-url-generation/index.html +69 -0
- data/site/public/docs/renee-url-generation/js/app.js +205 -0
- data/site/public/docs/renee-url-generation/js/full_list.js +167 -0
- data/site/public/docs/renee-url-generation/js/jquery.js +16 -0
- data/site/public/docs/renee-url-generation/method_list.html +590 -0
- data/site/public/docs/renee-url-generation/top-level-namespace.html +103 -0
- data/site/public/docs/renee/Renee.html +232 -0
- data/site/public/docs/renee/Renee/Application.html +367 -0
- data/site/public/docs/renee/Renee/Core.html +370 -0
- data/site/public/docs/renee/Renee/Core/Chaining.html +192 -0
- data/site/public/docs/renee/Renee/Core/ClassMethods.html +725 -0
- data/site/public/docs/renee/Renee/Core/ClientError.html +317 -0
- data/site/public/docs/renee/Renee/Core/EnvAccessors.html +152 -0
- data/site/public/docs/renee/Renee/Core/EnvAccessors/ClassMethods.html +354 -0
- data/site/public/docs/renee/Renee/Core/Matcher.html +675 -0
- data/site/public/docs/renee/Renee/Core/Plugins.html +475 -0
- data/site/public/docs/renee/Renee/Core/RackInteraction.html +488 -0
- data/site/public/docs/renee/Renee/Core/RequestContext.html +511 -0
- data/site/public/docs/renee/Renee/Core/Responding.html +877 -0
- data/site/public/docs/renee/Renee/Core/Response.html +691 -0
- data/site/public/docs/renee/Renee/Core/Routing.html +1589 -0
- data/site/public/docs/renee/Renee/Core/Transform.html +249 -0
- data/site/public/docs/renee/Renee/Core/URLGeneration.html +597 -0
- data/site/public/docs/renee/Renee/Render.html +877 -0
- data/site/public/docs/renee/Renee/Render/ClassMethods.html +382 -0
- data/site/public/docs/renee/Renee/Render/TemplateNotFound.html +126 -0
- data/site/public/docs/renee/Renee/Session.html +177 -0
- data/site/public/docs/renee/Renee/Session/ClassMethods.html +470 -0
- data/site/public/docs/renee/Renee/URLGeneration.html +142 -0
- data/site/public/docs/renee/Renee/URLGeneration/ClassMethods.html +593 -0
- data/site/public/docs/renee/Renee/Util.html +163 -0
- data/site/public/docs/renee/_index.html +336 -0
- data/site/public/docs/renee/class_list.html +47 -0
- data/site/public/docs/renee/css/common.css +1 -0
- data/site/public/docs/renee/css/full_list.css +55 -0
- data/site/public/docs/renee/css/style.css +322 -0
- data/site/public/docs/renee/file.README.html +212 -0
- data/site/public/docs/renee/file_list.html +49 -0
- data/site/public/docs/renee/frames.html +13 -0
- data/site/public/docs/renee/index.html +212 -0
- data/site/public/docs/renee/js/app.js +205 -0
- data/site/public/docs/renee/js/full_list.js +167 -0
- data/site/public/docs/renee/js/jquery.js +16 -0
- data/site/public/docs/renee/method_list.html +758 -0
- data/site/public/docs/renee/top-level-namespace.html +202 -0
- data/site/public/img/favicon.ico +0 -0
- data/site/public/img/reneeclean.png +0 -0
- data/site/public/img/russiangithub.png +0 -0
- data/site/public/img/stoneposter.png +0 -0
- data/site/public/img/vospit.jpeg +0 -0
- data/site/views/chaining.md +32 -0
- data/site/views/index.md +219 -0
- data/site/views/layouts/app.haml +16 -0
- data/site/views/rack-integration.md +51 -0
- data/site/views/responding.md +103 -0
- data/site/views/route-generation.md +82 -0
- data/site/views/routing.md +261 -0
- data/site/views/settings.md +19 -0
- data/site/views/team-renee.md +13 -0
- data/site/views/tutorial.md +57 -0
- data/site/views/variable-types.md +57 -0
- data/test.watchr +61 -0
- data/test/renee-core/chaining_test.rb +33 -0
- data/test/renee-core/env_accessors_test.rb +43 -0
- data/test/renee-core/include_test.rb +14 -0
- data/test/renee-core/request_context_test.rb +70 -0
- data/test/renee-core/responding_test.rb +128 -0
- data/test/renee-core/routing_test.rb +443 -0
- data/test/renee-core/test_helper.rb +4 -0
- data/test/renee-core/variable_type_test.rb +57 -0
- data/test/renee-render/render_test.rb +162 -0
- data/test/renee-render/test_helper.rb +9 -0
- data/test/renee-session/session_test.rb +31 -0
- data/test/renee-session/test_helper.rb +9 -0
- data/test/renee-url-generation/test_helper.rb +10 -0
- data/test/renee-url-generation/url_generation_test.rb +63 -0
- data/test/{blog_test.rb → renee/blog_test.rb} +10 -5
- data/test/renee/test_helper.rb +56 -0
- data/test/test_helper.rb +23 -10
- metadata +333 -156
- data/.yardopts +0 -6
@@ -0,0 +1,319 @@
|
|
1
|
+
module Renee
|
2
|
+
class Core
|
3
|
+
# Collection of useful methods for routing within a {Renee::Core} app.
|
4
|
+
module Routing
|
5
|
+
include Chaining
|
6
|
+
|
7
|
+
# Allow continued routing if a routing block fails to match
|
8
|
+
#
|
9
|
+
# @param [Boolean] val
|
10
|
+
# indicate if continued routing should be allowed.
|
11
|
+
#
|
12
|
+
# @api public
|
13
|
+
def continue_routing
|
14
|
+
if block_given?
|
15
|
+
original_env = @env.dup
|
16
|
+
begin
|
17
|
+
yield
|
18
|
+
rescue NotMatchedError
|
19
|
+
@env = original_env
|
20
|
+
end
|
21
|
+
else
|
22
|
+
create_chain_proxy(:continue_routing)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
chainable :continue_routing
|
26
|
+
|
27
|
+
# Match a path to respond to.
|
28
|
+
#
|
29
|
+
# @param [String] p
|
30
|
+
# path to match.
|
31
|
+
# @param [Proc] blk
|
32
|
+
# block to yield
|
33
|
+
#
|
34
|
+
# @example
|
35
|
+
# path('/') { ... } #=> '/'
|
36
|
+
# path('test') { ... } #=> '/test'
|
37
|
+
#
|
38
|
+
# path 'foo' do
|
39
|
+
# path('bar') { ... } #=> '/foo/bar'
|
40
|
+
# end
|
41
|
+
#
|
42
|
+
# @api public
|
43
|
+
def path(p, &blk)
|
44
|
+
if blk
|
45
|
+
p = p[1, p.size] if p[0] == ?/
|
46
|
+
extension_part = detected_extension ? "|\\.#{Regexp.quote(detected_extension)}" : ""
|
47
|
+
part(/^\/#{Regexp.quote(p)}(?=\/|$#{extension_part})/, &blk)
|
48
|
+
else
|
49
|
+
create_chain_proxy(:path, p)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
chainable :path
|
53
|
+
|
54
|
+
# Like #path, but doesn't look for leading slashes.
|
55
|
+
def part(p)
|
56
|
+
if block_given?
|
57
|
+
p = /^\/?#{Regexp.quote(p)}/ if p.is_a?(String)
|
58
|
+
if match = env['PATH_INFO'][p]
|
59
|
+
with_path_part(match) { yield }
|
60
|
+
end
|
61
|
+
else
|
62
|
+
create_chain_proxy(:part, p)
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
# Match parts off the path as variables. The parts matcher can conform to either a regular expression, or be an Integer, or
|
67
|
+
# simply a String.
|
68
|
+
# @param[Object] type the type of object to match for. If you supply Integer, this will only match integers in addition to casting your variable for you.
|
69
|
+
# @param[Object] default the default value to use if your param cannot be successfully matched.
|
70
|
+
#
|
71
|
+
# @example
|
72
|
+
# path '/' do
|
73
|
+
# variable { |id| halt [200, {}, id] }
|
74
|
+
# end
|
75
|
+
# GET /hey #=> [200, {}, 'hey']
|
76
|
+
#
|
77
|
+
# @example
|
78
|
+
# path '/' do
|
79
|
+
# variable(:integer) { |id| halt [200, {}, "This is a numeric id: #{id}"] }
|
80
|
+
# end
|
81
|
+
# GET /123 #=> [200, {}, 'This is a numeric id: 123']
|
82
|
+
#
|
83
|
+
# @example
|
84
|
+
# path '/test' do
|
85
|
+
# variable { |foo, bar| halt [200, {}, "#{foo}-#{bar}"] }
|
86
|
+
# end
|
87
|
+
# GET /test/hey/there #=> [200, {}, 'hey-there']
|
88
|
+
#
|
89
|
+
# @api public
|
90
|
+
def variable(type = nil, &blk)
|
91
|
+
blk ? complex_variable(type, '/', 1, &blk) : create_chain_proxy(:variable, type)
|
92
|
+
end
|
93
|
+
alias_method :var, :variable
|
94
|
+
chainable :variable, :var
|
95
|
+
|
96
|
+
def optional_variable(type = nil, &blk)
|
97
|
+
blk ? complex_variable(type, '/', 0..1) { |vars| blk[vars.first] } : create_chain_proxy(:variable, type)
|
98
|
+
end
|
99
|
+
alias_method :optional, :optional_variable
|
100
|
+
chainable :optional, :optional_variable
|
101
|
+
|
102
|
+
# Same as variable except you can match multiple variables with the same type.
|
103
|
+
# @param [Range, Integer] count The number of parameters to capture.
|
104
|
+
# @param [Symbol] type The type to use for match.
|
105
|
+
def multi_variable(count, type = nil, &blk)
|
106
|
+
blk ? complex_variable(type, '/', count, &blk) : create_chain_proxy(:multi_variable, count, type)
|
107
|
+
end
|
108
|
+
alias_method :multi_var, :multi_variable
|
109
|
+
alias_method :mvar, :multi_variable
|
110
|
+
chainable :multi_variable, :multi_var, :mvar
|
111
|
+
|
112
|
+
# Same as variable except it matches indefinitely.
|
113
|
+
# @param [Symbol] type The type to use for match.
|
114
|
+
def repeating_variable(type = nil, &blk)
|
115
|
+
blk ? complex_variable(type, '/', nil, &blk) : create_chain_proxy(:repeating_variable, type)
|
116
|
+
end
|
117
|
+
alias_method :glob, :repeating_variable
|
118
|
+
chainable :repeating_variable, :glob
|
119
|
+
|
120
|
+
# Match parts off the path as variables without a leading slash.
|
121
|
+
# @see #variable
|
122
|
+
# @api public
|
123
|
+
def partial_variable(type = nil, &blk)
|
124
|
+
blk ? complex_variable(type, nil, 1, &blk) : create_chain_proxy(:partial_variable, type)
|
125
|
+
end
|
126
|
+
alias_method :part_var, :partial_variable
|
127
|
+
chainable :partial_variable, :part_var
|
128
|
+
|
129
|
+
# Returns the matched extension. If no extension is present, returns `nil`.
|
130
|
+
#
|
131
|
+
# @example
|
132
|
+
# halt [200, {}, path] if extension == 'html'
|
133
|
+
#
|
134
|
+
# @api public
|
135
|
+
def extension
|
136
|
+
detected_extension
|
137
|
+
end
|
138
|
+
alias_method :ext, :extension
|
139
|
+
|
140
|
+
# Match no extension.
|
141
|
+
#
|
142
|
+
# @example
|
143
|
+
# no_extension { |path| halt [200, {}, path] }
|
144
|
+
#
|
145
|
+
# @api public
|
146
|
+
def no_extension(&blk)
|
147
|
+
blk.call unless detected_extension
|
148
|
+
end
|
149
|
+
|
150
|
+
# Match any remaining path.
|
151
|
+
#
|
152
|
+
# @example
|
153
|
+
# remainder { |path| halt [200, {}, path] }
|
154
|
+
#
|
155
|
+
# @api public
|
156
|
+
def remainder(&blk)
|
157
|
+
blk ? with_path_part(env['PATH_INFO']) { |var| blk.call(var) } : create_chain_proxy(:remainder)
|
158
|
+
end
|
159
|
+
alias_method :catchall, :remainder
|
160
|
+
chainable :remainder, :catchall
|
161
|
+
|
162
|
+
# Respond to a GET request and yield the block.
|
163
|
+
#
|
164
|
+
# @example
|
165
|
+
# get { halt [200, {}, "hello world"] }
|
166
|
+
#
|
167
|
+
# @api public
|
168
|
+
def get(&blk)
|
169
|
+
blk ? request_method('GET', &blk) : create_chain_proxy(:get)
|
170
|
+
end
|
171
|
+
chainable :get
|
172
|
+
|
173
|
+
# Respond to a POST request and yield the block.
|
174
|
+
#
|
175
|
+
# @example
|
176
|
+
# post { halt [200, {}, "hello world"] }
|
177
|
+
#
|
178
|
+
# @api public
|
179
|
+
def post(&blk)
|
180
|
+
blk ? request_method('POST', &blk) : create_chain_proxy(:post)
|
181
|
+
end
|
182
|
+
chainable :post
|
183
|
+
|
184
|
+
# Respond to a PUT request and yield the block.
|
185
|
+
#
|
186
|
+
# @example
|
187
|
+
# put { halt [200, {}, "hello world"] }
|
188
|
+
#
|
189
|
+
# @api public
|
190
|
+
def put(&blk)
|
191
|
+
blk ? request_method('PUT', &blk) : create_chain_proxy(:put)
|
192
|
+
end
|
193
|
+
chainable :put
|
194
|
+
|
195
|
+
# Respond to a DELETE request and yield the block.
|
196
|
+
#
|
197
|
+
# @example
|
198
|
+
# delete { halt [200, {}, "hello world"] }
|
199
|
+
#
|
200
|
+
# @api public
|
201
|
+
def delete(&blk)
|
202
|
+
blk ? request_method('DELETE', &blk) : create_chain_proxy(:delete)
|
203
|
+
end
|
204
|
+
chainable :delete
|
205
|
+
|
206
|
+
# Match only when the path is either '' or '/'.
|
207
|
+
#
|
208
|
+
# @example
|
209
|
+
# complete { halt [200, {}, "hello world"] }
|
210
|
+
#
|
211
|
+
# @api public
|
212
|
+
def complete(&blk)
|
213
|
+
if blk
|
214
|
+
with_path_part(env['PATH_INFO']) { blk.call } if complete?
|
215
|
+
else
|
216
|
+
create_chain_proxy(:complete)
|
217
|
+
end
|
218
|
+
end
|
219
|
+
chainable :complete
|
220
|
+
|
221
|
+
# Test if the path has been consumed
|
222
|
+
#
|
223
|
+
# @example
|
224
|
+
# if complete?
|
225
|
+
# halt "Hey, the path is done"
|
226
|
+
# end
|
227
|
+
#
|
228
|
+
# @api public
|
229
|
+
def complete?
|
230
|
+
(detected_extension and env['PATH_INFO'] =~ /^\/?(\.#{Regexp.quote(detected_extension)}\/?)?$/) || (detected_extension.nil? and env['PATH_INFO'] =~ /^\/?$/)
|
231
|
+
end
|
232
|
+
|
233
|
+
# Match only when the path is ''.
|
234
|
+
#
|
235
|
+
# @example
|
236
|
+
# empty { halt [200, {}, "hello world"] }
|
237
|
+
#
|
238
|
+
# @api public
|
239
|
+
def empty(&blk)
|
240
|
+
if blk
|
241
|
+
if env['PATH_INFO'] == ''
|
242
|
+
with_path_part(env['PATH_INFO']) { blk.call }
|
243
|
+
end
|
244
|
+
else
|
245
|
+
create_chain_proxy(:empty)
|
246
|
+
end
|
247
|
+
end
|
248
|
+
chainable :empty
|
249
|
+
|
250
|
+
private
|
251
|
+
def complex_variable(type, prefix, count)
|
252
|
+
matcher = variable_matcher_for_type(type)
|
253
|
+
path = env['PATH_INFO'].dup
|
254
|
+
vals = []
|
255
|
+
var_index = 0
|
256
|
+
variable_matching_loop(count) do
|
257
|
+
path.start_with?(prefix) ? path.slice!(0, prefix.size) : break if prefix
|
258
|
+
if match = matcher[path]
|
259
|
+
path.slice!(0, match.first.size)
|
260
|
+
vals << match.last
|
261
|
+
end
|
262
|
+
end
|
263
|
+
return unless count.nil? || count === vals.size
|
264
|
+
with_path_part(env['PATH_INFO'][0, env['PATH_INFO'].size - path.size]) do
|
265
|
+
if count == 1
|
266
|
+
yield(vals.first)
|
267
|
+
else
|
268
|
+
yield(vals)
|
269
|
+
end
|
270
|
+
end
|
271
|
+
end
|
272
|
+
|
273
|
+
def variable_matching_loop(count)
|
274
|
+
case count
|
275
|
+
when Range then count.max.times { break unless yield }
|
276
|
+
when nil then loop { break unless yield }
|
277
|
+
else count.times { break unless yield }
|
278
|
+
end
|
279
|
+
end
|
280
|
+
|
281
|
+
def variable_matcher_for_type(type)
|
282
|
+
if self.class.variable_types.key?(type)
|
283
|
+
self.class.variable_types[type]
|
284
|
+
else
|
285
|
+
regexp = case type
|
286
|
+
when nil, String
|
287
|
+
detected_extension ?
|
288
|
+
/(([^\/](?!#{Regexp.quote(detected_extension)}$))+)(?=$|\/|\.#{Regexp.quote(detected_extension)})/ :
|
289
|
+
/([^\/]+)(?=$|\/)/
|
290
|
+
when Regexp
|
291
|
+
type
|
292
|
+
else
|
293
|
+
raise "Unexpected variable type #{type.inspect}"
|
294
|
+
end
|
295
|
+
proc do |path|
|
296
|
+
if match = /^#{regexp.to_s}/.match(path)
|
297
|
+
[match[0]]
|
298
|
+
end
|
299
|
+
end
|
300
|
+
end
|
301
|
+
end
|
302
|
+
|
303
|
+
def with_path_part(part)
|
304
|
+
script_part = env['PATH_INFO'][0, part.size]
|
305
|
+
env['PATH_INFO'] = env['PATH_INFO'].slice(part.size, env['PATH_INFO'].size)
|
306
|
+
env['SCRIPT_NAME'] += script_part
|
307
|
+
yield script_part
|
308
|
+
raise NotMatchedError
|
309
|
+
end
|
310
|
+
|
311
|
+
def request_method(method)
|
312
|
+
if env['REQUEST_METHOD'] == method && complete?
|
313
|
+
yield
|
314
|
+
raise NotMatchedError
|
315
|
+
end
|
316
|
+
end
|
317
|
+
end
|
318
|
+
end
|
319
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module Renee
|
2
|
+
class Core
|
3
|
+
# Module used for transforming arbitrary values using the registerd variable types.
|
4
|
+
# @see #register_variable_name.
|
5
|
+
#
|
6
|
+
module Transform
|
7
|
+
# Transforms a value according to the rules specified by #register_variable_name.
|
8
|
+
# @param [Symbol] name The name of the variable type.
|
9
|
+
# @param [String] value The value to transform.
|
10
|
+
# @return The transformed value or nil.
|
11
|
+
def transform(type, value)
|
12
|
+
if self.class.variable_types.key?(type) and m = self.class.variable_types[type][value]
|
13
|
+
m.first == value ? m.last : nil
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
data/lib/renee/render.rb
ADDED
@@ -0,0 +1,221 @@
|
|
1
|
+
require 'tilt'
|
2
|
+
require 'callsite'
|
3
|
+
|
4
|
+
require 'renee/version'
|
5
|
+
|
6
|
+
# Top-level Renee constant
|
7
|
+
module Renee
|
8
|
+
# This module is responsible for handling the rendering of templates
|
9
|
+
# using Tilt supporting all included template engines.
|
10
|
+
module Render
|
11
|
+
# Current version of Renee::Render
|
12
|
+
VERSION = Renee::VERSION
|
13
|
+
|
14
|
+
def self.included(o)
|
15
|
+
o.extend(ClassMethods)
|
16
|
+
end
|
17
|
+
|
18
|
+
# Class-methods included by this module.
|
19
|
+
module ClassMethods
|
20
|
+
# Gets or sets the views_path for an application.
|
21
|
+
#
|
22
|
+
# @param [String] path The path to the view files.
|
23
|
+
#
|
24
|
+
# @example
|
25
|
+
# views_path("./views") => nil
|
26
|
+
# views_path => "./views"
|
27
|
+
#
|
28
|
+
# @api public
|
29
|
+
def views_path(path = nil)
|
30
|
+
path ? @views_path = path : @views_path
|
31
|
+
end
|
32
|
+
|
33
|
+
# Gets or sets the default encoding used.
|
34
|
+
#
|
35
|
+
# @param [String] encoding The encoding to use. e.g. "utf-8"
|
36
|
+
def default_encoding(encoding = nil)
|
37
|
+
encoding ? @encoding = encoding : @encoding
|
38
|
+
end
|
39
|
+
|
40
|
+
# Gets or sets the default layout used.
|
41
|
+
#
|
42
|
+
# @param [String] layout The layout to use. e.g. "application.haml"
|
43
|
+
def default_layout(*args)
|
44
|
+
case args.size
|
45
|
+
when 0 then @layout
|
46
|
+
when 1 then @layout = args.first
|
47
|
+
else raise
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
##
|
53
|
+
# Exception responsible for when an expected template does not exist.
|
54
|
+
#
|
55
|
+
class TemplateNotFound < RuntimeError; end
|
56
|
+
|
57
|
+
# Same as render but automatically halts.
|
58
|
+
# @param (see #render)
|
59
|
+
# @return (see #render)
|
60
|
+
# @see #render
|
61
|
+
def render!(file, engine = nil, options = nil, &blk)
|
62
|
+
halt render(file, engine, options, &blk)
|
63
|
+
end
|
64
|
+
|
65
|
+
# Same as inline but automatically halts.
|
66
|
+
# @param (see #inline)
|
67
|
+
# @return (see #inline)
|
68
|
+
# @see #inline
|
69
|
+
def inline!(data, engine, options = {}, &blk)
|
70
|
+
options[:_caller] = Callsite.parse(caller.first)
|
71
|
+
halt inline(data, engine, options, &blk)
|
72
|
+
end
|
73
|
+
|
74
|
+
##
|
75
|
+
# Renders a file given the engine and the content.
|
76
|
+
#
|
77
|
+
# @param [String] file The path to the file to render
|
78
|
+
# @param [Symbol] engine The name of the engine to use to render the content. If this isn't specified
|
79
|
+
# it will be detected based on the extension of the file.
|
80
|
+
# @param [Hash] options The options to pass in for rendering.
|
81
|
+
#
|
82
|
+
# @return [String] The result of rendering the data with specified engine.
|
83
|
+
#
|
84
|
+
# @example
|
85
|
+
# render "index", :haml # => "<p>test</p>"
|
86
|
+
# render "index" # => "<p>test</p>"
|
87
|
+
#
|
88
|
+
# @api public
|
89
|
+
#
|
90
|
+
def render(file, engine = nil, options = nil, &block)
|
91
|
+
options, engine = engine, nil if engine.is_a?(Hash)
|
92
|
+
render_setup(engine, options, block) do |view_options, views|
|
93
|
+
template_cache.fetch(engine, file, view_options) do
|
94
|
+
file_path, found_engine = find_template(views, file, engine)
|
95
|
+
template = Tilt[found_engine]
|
96
|
+
raise TemplateNotFound, "Template engine not found: #{found_engine.inspect}" unless template
|
97
|
+
raise TemplateNotFound, "Template #{file.inspect} (with engine #{engine.inspect}) not found in #{views.inspect}!" unless file_path
|
98
|
+
# TODO suppress errors for layouts?
|
99
|
+
template.new(file_path, 1, view_options)
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end # render
|
103
|
+
|
104
|
+
##
|
105
|
+
# Renders a string given the engine and the content.
|
106
|
+
#
|
107
|
+
# @param [String] data The string data to render.
|
108
|
+
# @param [Symbol] engine The name of the engine to use to render the content. If this isn't specified
|
109
|
+
# it will be detected based on the extension of the file.
|
110
|
+
# @param [Hash] options The options to pass in for rendering.
|
111
|
+
#
|
112
|
+
# @return [String] The result of rendering the data with specified engine.
|
113
|
+
#
|
114
|
+
# @example
|
115
|
+
# inline "%p test", :haml # => "<p>test</p>"
|
116
|
+
#
|
117
|
+
# @api public
|
118
|
+
#
|
119
|
+
def inline(data, engine, options = nil, &block)
|
120
|
+
options, engine = engine, nil if engine.is_a?(Hash)
|
121
|
+
call_data = options && options.delete(:_caller) || Callsite.parse(caller.first)
|
122
|
+
render_setup(engine, options, block) do |view_options, views|
|
123
|
+
body = data.is_a?(Proc) ? data : Proc.new { data }
|
124
|
+
template = Tilt[engine]
|
125
|
+
raise "Template engine not found: #{engine}" if template.nil?
|
126
|
+
template.new(call_data.filename, call_data.line, view_options, &body)
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
##
|
131
|
+
# Render a partials with collections support
|
132
|
+
#
|
133
|
+
# @return [String] The html generated from this partial.
|
134
|
+
#
|
135
|
+
# @example
|
136
|
+
# partial 'photo/item', :object => @photo
|
137
|
+
# partial 'photo/item', :collection => @photos
|
138
|
+
# partial 'photo/item', :locals => { :foo => :bar }
|
139
|
+
#
|
140
|
+
def partial(template, options={})
|
141
|
+
options = { :locals => {}, :layout => false }.merge(options)
|
142
|
+
path = template.to_s.split(File::SEPARATOR)
|
143
|
+
object_name = path[-1].to_sym
|
144
|
+
path[-1] = "_#{path[-1]}"
|
145
|
+
template_path = File.join(path).to_sym
|
146
|
+
raise 'Partial collection specified but is nil' if options.has_key?(:collection) && options[:collection].nil?
|
147
|
+
if collection = options.delete(:collection)
|
148
|
+
options.delete(:object)
|
149
|
+
counter = 0
|
150
|
+
collection.map { |member|
|
151
|
+
counter += 1
|
152
|
+
options[:locals].merge!(object_name => member, "#{object_name}_counter".to_sym => counter)
|
153
|
+
render(template_path, options.dup)
|
154
|
+
}.join("\n")
|
155
|
+
else
|
156
|
+
if member = options.delete(:object)
|
157
|
+
options[:locals].merge!(object_name => member)
|
158
|
+
end
|
159
|
+
render(template_path, options.dup)
|
160
|
+
end
|
161
|
+
end
|
162
|
+
|
163
|
+
private
|
164
|
+
|
165
|
+
|
166
|
+
def render_setup(engine, options, block)
|
167
|
+
options ||= {}
|
168
|
+
options[:outvar] ||= '@_out_buf'
|
169
|
+
options[:default_encoding] ||= self.class.default_encoding || options[:encoding] || "utf-8"
|
170
|
+
|
171
|
+
locals = options.delete(:locals) || {}
|
172
|
+
views = options.delete(:views) || self.class.views_path || "./views"
|
173
|
+
layout = options.key?(:layout) ? options[:layout] : self.class.default_layout
|
174
|
+
layout_engine = options.delete(:layout_engine)
|
175
|
+
# TODO suppress template errors for layouts?
|
176
|
+
# TODO allow content_type to be set with an option to render?
|
177
|
+
scope = options.delete(:scope) || self
|
178
|
+
|
179
|
+
# TODO default layout file convention?
|
180
|
+
template = yield(options, views)
|
181
|
+
output = template.render(scope, locals, &block)
|
182
|
+
|
183
|
+
if layout # render layout
|
184
|
+
# TODO handle when layout is missing better!
|
185
|
+
options = options.merge(:views => views, :layout => false, :scope => scope)
|
186
|
+
render(layout, layout_engine, options.merge(:locals => locals)) { output }
|
187
|
+
else
|
188
|
+
output
|
189
|
+
end
|
190
|
+
end
|
191
|
+
|
192
|
+
##
|
193
|
+
# Searches view paths for template based on data and engine with rendering options.
|
194
|
+
# Supports finding a template without an engine.
|
195
|
+
#
|
196
|
+
# @param [String] views The view paths
|
197
|
+
# @param [String] name The name of the template
|
198
|
+
# @param [Symbol] engine The engine to use for rendering.
|
199
|
+
#
|
200
|
+
# @return [<String, Symbol>] An array of the file path and the engine.
|
201
|
+
#
|
202
|
+
# @example
|
203
|
+
# find_template("./views", "index", :erb) => ["path/to/index.erb", :erb]
|
204
|
+
# find_template("./views", "foo") => ["path/to/index.haml", :haml]
|
205
|
+
#
|
206
|
+
# @api private
|
207
|
+
#
|
208
|
+
def find_template(views, name, engine=nil)
|
209
|
+
lookup_ext = (engine || File.extname(name.to_s)[1..-1] || "*").to_s
|
210
|
+
base_name = name.to_s.chomp(".#{lookup_ext}")
|
211
|
+
file_path = Dir[File.expand_path("#{base_name}.#{lookup_ext}", views)].first
|
212
|
+
engine ||= File.extname(file_path)[1..-1].to_sym if file_path
|
213
|
+
[file_path, engine]
|
214
|
+
end # find_template
|
215
|
+
|
216
|
+
# Maintain Tilt::Cache of the templates.
|
217
|
+
def template_cache
|
218
|
+
@template_cache ||= Tilt::Cache.new
|
219
|
+
end
|
220
|
+
end
|
221
|
+
end
|