esi-for-rack 0.0.4
Sign up to get free protection for your applications and to get access to all the features.
- data/README.rdoc +34 -0
- data/Rakefile +34 -0
- data/VERSION +1 -0
- data/lib/esi_for_rack.rb +81 -0
- data/lib/esi_for_rack/lookup.rb +105 -0
- data/lib/esi_for_rack/node.rb +161 -0
- data/spec/http_integration/accept_language_spec.rb +23 -0
- data/spec/http_integration/cookie_spec.rb +23 -0
- data/spec/http_integration/fixtures/accept_language/1.html +12 -0
- data/spec/http_integration/fixtures/cookie/1.html +3 -0
- data/spec/http_integration/fixtures/query_string/1.html +3 -0
- data/spec/http_integration/fixtures/user_agent/1.html +5 -0
- data/spec/http_integration/query_string_spec.rb +22 -0
- data/spec/http_integration/user_agent_spec.rb +23 -0
- data/spec/spec.opts +7 -0
- data/spec/spec_helper.rb +25 -0
- data/spec/tags/choose_spec.rb +18 -0
- data/spec/tags/comment_spec.rb +9 -0
- data/spec/tags/fixtures/choose/simple1.html +8 -0
- data/spec/tags/fixtures/choose/simple2.html +11 -0
- data/spec/tags/fixtures/choose/simple3.html +8 -0
- data/spec/tags/fixtures/comment/simple.html +1 -0
- data/spec/tags/fixtures/include/alt.html +3 -0
- data/spec/tags/fixtures/include/src.html +3 -0
- data/spec/tags/fixtures/include/src_continue.html +3 -0
- data/spec/tags/fixtures/nested/complex.html +12 -0
- data/spec/tags/fixtures/try/include.html +6 -0
- data/spec/tags/fixtures/try/malformed_attempt.html +5 -0
- data/spec/tags/fixtures/try/malformed_except.html +3 -0
- data/spec/tags/fixtures/vars/simple.html +1 -0
- data/spec/tags/include_spec.rb +25 -0
- data/spec/tags/nested_spec.rb +13 -0
- data/spec/tags/try_spec.rb +28 -0
- data/spec/tags/vars_spec.rb +9 -0
- metadata +137 -0
data/README.rdoc
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
= EsiForRack
|
2
|
+
|
3
|
+
== What is ESI?
|
4
|
+
|
5
|
+
ESI is a standard way to assemble pages. This spec is supported by several reverse proxies.
|
6
|
+
|
7
|
+
http://slideshare.net/joshbuddy/to-the-edge-of-web-performance-and-beyond
|
8
|
+
|
9
|
+
== Usage
|
10
|
+
|
11
|
+
In your builder, just use it.
|
12
|
+
|
13
|
+
use EsiForRack
|
14
|
+
|
15
|
+
In your HTTP responses, just normal ESI tags. If you're working within Rails, give Spackle a try.
|
16
|
+
|
17
|
+
http://github.com/joshbuddy/spackle
|
18
|
+
|
19
|
+
Here is an example of a response that would be parsed by EsiForRack
|
20
|
+
|
21
|
+
<html>
|
22
|
+
<body>
|
23
|
+
<esi:include src="/helloworld"/>
|
24
|
+
</body>
|
25
|
+
</html>
|
26
|
+
|
27
|
+
In this case, a request to <tt>/helloworld</tt> would be made by EsiForRack to fill in the request. If your application sent: "Hey world" as a response to <tt>/helloworld</tt> the above example would be interpolated to:
|
28
|
+
|
29
|
+
<html>
|
30
|
+
<body>
|
31
|
+
Hey world
|
32
|
+
</body>
|
33
|
+
</html>
|
34
|
+
|
data/Rakefile
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
require 'lib/esi_for_rack'
|
2
|
+
|
3
|
+
begin
|
4
|
+
require 'jeweler'
|
5
|
+
Jeweler::Tasks.new do |s|
|
6
|
+
s.name = "esi-for-rack"
|
7
|
+
s.description = s.summary = "ESI for Rack"
|
8
|
+
s.email = "joshbuddy@gmail.com"
|
9
|
+
s.homepage = "http://github.com/joshbuddy/esi_for_rack"
|
10
|
+
s.authors = ["Joshua Hull"]
|
11
|
+
s.files = FileList["[A-Z]*", "{lib,spec}/**/*"]
|
12
|
+
s.add_dependency 'esi_attribute_language'
|
13
|
+
s.add_dependency 'nokogiri'
|
14
|
+
s.add_dependency 'spy-vs-spy'
|
15
|
+
s.add_dependency 'dirge'
|
16
|
+
end
|
17
|
+
Jeweler::GemcutterTasks.new
|
18
|
+
rescue LoadError
|
19
|
+
puts "Jeweler not available. Install it with: sudo gem install technicalpickles-jeweler -s http://gems.github.com"
|
20
|
+
end
|
21
|
+
|
22
|
+
require 'spec'
|
23
|
+
require 'spec/rake/spectask'
|
24
|
+
|
25
|
+
task :spec => 'spec:all'
|
26
|
+
namespace(:spec) do
|
27
|
+
Spec::Rake::SpecTask.new(:all) do |t|
|
28
|
+
t.spec_opts ||= []
|
29
|
+
t.spec_opts << "-rubygems"
|
30
|
+
t.spec_opts << "--options" << "spec/spec.opts"
|
31
|
+
t.spec_files = FileList['spec/**/*.rb']
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.0.4
|
data/lib/esi_for_rack.rb
ADDED
@@ -0,0 +1,81 @@
|
|
1
|
+
require 'nokogiri'
|
2
|
+
require 'set'
|
3
|
+
|
4
|
+
require 'esi_attribute_language'
|
5
|
+
require 'spy_vs_spy'
|
6
|
+
require 'dirge'
|
7
|
+
|
8
|
+
require ~'esi_for_rack/node'
|
9
|
+
require ~'esi_for_rack/lookup'
|
10
|
+
|
11
|
+
class EsiForRack
|
12
|
+
|
13
|
+
def initialize(app, lookup = nil)
|
14
|
+
@app = app
|
15
|
+
@lookup = lookup
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.response_body_to_str(response_body)
|
19
|
+
if response_body.respond_to? :to_str
|
20
|
+
response_body.to_str
|
21
|
+
elsif response_body.respond_to?(:each)
|
22
|
+
body = ''
|
23
|
+
response_body.each { |part|
|
24
|
+
body << part.to_s
|
25
|
+
}
|
26
|
+
body
|
27
|
+
else
|
28
|
+
raise TypeError, "stringable or iterable required"
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def call(env)
|
33
|
+
@lookup ||= [Lookup::PassThrough.new(@app, env), Lookup::Http.new(@app, env)]
|
34
|
+
request = Rack::Request.new(env)
|
35
|
+
result = @app.call(env)
|
36
|
+
response = Rack::Response.new(result[2], result[0], result[1])
|
37
|
+
|
38
|
+
if response['Content-Type'] =~ /text\/html/ && (body = EsiForRack.response_body_to_str(result.last)) && body.index('<esi:')
|
39
|
+
user_agent_hash = {}
|
40
|
+
begin
|
41
|
+
user_agent = SOC::SpyVsSpy.new(env['HTTP_USER_AGENT'] || '-')
|
42
|
+
user_agent_hash['browser'] = case user_agent.browser
|
43
|
+
when 'Firefox' then 'MOZILLA'
|
44
|
+
when 'MSIE' then 'MSIE'
|
45
|
+
else; 'OTHER'
|
46
|
+
end
|
47
|
+
|
48
|
+
user_agent_hash['version'] = [user_agent.version.major, user_agent.version.minor].compact.join('.')
|
49
|
+
|
50
|
+
user_agent_hash['os'] = if user_agent.os.windows?
|
51
|
+
'WIN'
|
52
|
+
elsif user_agent.os.osx?
|
53
|
+
'MAC'
|
54
|
+
else
|
55
|
+
'OTHER'
|
56
|
+
end
|
57
|
+
rescue
|
58
|
+
# error parsing ua
|
59
|
+
end
|
60
|
+
|
61
|
+
binding = {
|
62
|
+
:HTTP_ACCEPT_LANGUAGE => Set.new((env['HTTP_ACCEPT_LANGUAGE'] || '').split(',').map{|l| l.gsub(/q=[0-9]\.[0-9]{1,3}/, '').gsub(';','').strip}),
|
63
|
+
:HTTP_COOKIE => request.cookies,
|
64
|
+
:HTTP_HOST => request.host,
|
65
|
+
:HTTP_REFERER => request.referer,
|
66
|
+
:HTTP_USER_AGENT => user_agent_hash,
|
67
|
+
:QUERY_STRING => request.GET
|
68
|
+
}
|
69
|
+
context = Node::Context.new(binding, @lookup)
|
70
|
+
|
71
|
+
parsed_body = context.parse(body).to_s
|
72
|
+
response.header['Content-Length'] = parsed_body.size.to_s
|
73
|
+
|
74
|
+
[response.status, response.headers, [parsed_body]]
|
75
|
+
else
|
76
|
+
result
|
77
|
+
end
|
78
|
+
|
79
|
+
end
|
80
|
+
|
81
|
+
end
|
@@ -0,0 +1,105 @@
|
|
1
|
+
require 'net/http'
|
2
|
+
require 'uri'
|
3
|
+
require 'logger'
|
4
|
+
|
5
|
+
class EsiForRack
|
6
|
+
|
7
|
+
class Lookup
|
8
|
+
|
9
|
+
class Http
|
10
|
+
|
11
|
+
class RedirectFollower
|
12
|
+
class TooManyRedirects < StandardError; end
|
13
|
+
|
14
|
+
attr_accessor :url, :body, :redirect_limit, :response, :code
|
15
|
+
|
16
|
+
def initialize(url, limit=5)
|
17
|
+
@url, @redirect_limit = url, limit
|
18
|
+
logger.level = Logger::INFO
|
19
|
+
end
|
20
|
+
|
21
|
+
def logger
|
22
|
+
@logger ||= Logger.new(STDOUT)
|
23
|
+
end
|
24
|
+
|
25
|
+
def resolve
|
26
|
+
raise TooManyRedirects if redirect_limit < 0
|
27
|
+
|
28
|
+
self.response = Net::HTTP.get_response(URI.parse(url))
|
29
|
+
|
30
|
+
logger.info "redirect limit: #{redirect_limit}"
|
31
|
+
logger.info "response code: #{response.code}"
|
32
|
+
logger.debug "response body: #{response.body}"
|
33
|
+
|
34
|
+
if response.kind_of?(Net::HTTPRedirection)
|
35
|
+
self.url = redirect_url
|
36
|
+
self.redirect_limit -= 1
|
37
|
+
|
38
|
+
logger.info "redirect found, headed to #{url}"
|
39
|
+
resolve
|
40
|
+
end
|
41
|
+
|
42
|
+
self.body = response.body
|
43
|
+
self.code = response.code
|
44
|
+
self
|
45
|
+
end
|
46
|
+
|
47
|
+
def redirect_url
|
48
|
+
if response['location'].nil?
|
49
|
+
response.body.match(/<a href=\"([^>]+)\">/i)[1]
|
50
|
+
else
|
51
|
+
response['location']
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
|
57
|
+
def initialize(app, env)
|
58
|
+
@app = app
|
59
|
+
@env = env
|
60
|
+
end
|
61
|
+
|
62
|
+
def [](path)
|
63
|
+
res = RedirectFollower.new(path).resolve
|
64
|
+
res.body if res.code == '200'
|
65
|
+
end
|
66
|
+
|
67
|
+
end
|
68
|
+
|
69
|
+
class PassThrough
|
70
|
+
|
71
|
+
def initialize(app, env)
|
72
|
+
@app = app
|
73
|
+
@env = env
|
74
|
+
end
|
75
|
+
|
76
|
+
def [](path)
|
77
|
+
return if path[0,4] == 'http'
|
78
|
+
|
79
|
+
uri = URI(path)
|
80
|
+
|
81
|
+
request = {
|
82
|
+
"REQUEST_METHOD" => "GET",
|
83
|
+
"SERVER_NAME" => @env['SERVER_NAME'],
|
84
|
+
"SERVER_PORT" => @env['SERVER_PORT'],
|
85
|
+
"QUERY_STRING" => uri.query.to_s,
|
86
|
+
"PATH_INFO" => (!uri.path || uri.path.empty?) ? "/" : uri.path,
|
87
|
+
"rack.url_scheme" => uri.scheme || @env['rack.url_scheme'] || 'http',
|
88
|
+
"SCRIPT_NAME" => ""
|
89
|
+
}
|
90
|
+
|
91
|
+
response = @app.call(request)
|
92
|
+
if response.first == 200
|
93
|
+
EsiForRack.response_body_to_str(response.last)
|
94
|
+
else
|
95
|
+
nil
|
96
|
+
end
|
97
|
+
|
98
|
+
rescue URI::InvalidURIError
|
99
|
+
nil
|
100
|
+
end
|
101
|
+
|
102
|
+
end
|
103
|
+
|
104
|
+
end
|
105
|
+
end
|
@@ -0,0 +1,161 @@
|
|
1
|
+
class EsiForRack
|
2
|
+
|
3
|
+
class Node
|
4
|
+
|
5
|
+
IncludeFailedError = Class.new(RuntimeError)
|
6
|
+
|
7
|
+
attr_reader :node, :context
|
8
|
+
|
9
|
+
def init(node, context)
|
10
|
+
@node = node
|
11
|
+
@context = context
|
12
|
+
self
|
13
|
+
end
|
14
|
+
|
15
|
+
def execute_in_place!
|
16
|
+
node.replace(Nokogiri::XML::CDATA.new(node.document, execute.to_s))
|
17
|
+
end
|
18
|
+
|
19
|
+
class Include < Node
|
20
|
+
|
21
|
+
def resolved_src
|
22
|
+
EsiAttributeLanguage::SimpleGrammar.parse(@node['src']).execute(context.resolver)
|
23
|
+
end
|
24
|
+
|
25
|
+
def resolved_alt
|
26
|
+
EsiAttributeLanguage::SimpleGrammar.parse(@node['alt']).execute(context.resolver) if @node['alt']
|
27
|
+
end
|
28
|
+
|
29
|
+
def continue_on_error?
|
30
|
+
node['onerror'] == 'continue'
|
31
|
+
end
|
32
|
+
|
33
|
+
def execute
|
34
|
+
context.lookup(resolved_src) or
|
35
|
+
(resolved_alt && context.lookup(resolved_alt)) or
|
36
|
+
(!continue_on_error? && raise(IncludeFailedError.new)) or nil
|
37
|
+
end
|
38
|
+
|
39
|
+
end
|
40
|
+
|
41
|
+
class Vars < Node
|
42
|
+
def execute
|
43
|
+
EsiAttributeLanguage::SimpleGrammar.parse(node.to_str).execute(context.resolver)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
class Try < Node
|
48
|
+
|
49
|
+
def execute
|
50
|
+
unless @esi_attempt = node.css('esi_attempt')[0]
|
51
|
+
raise "no attempt within try"
|
52
|
+
end
|
53
|
+
|
54
|
+
unless @esi_except = node.css('esi_except')[0]
|
55
|
+
raise "no except within try"
|
56
|
+
end
|
57
|
+
|
58
|
+
val = ''
|
59
|
+
begin
|
60
|
+
context.process(@esi_attempt)
|
61
|
+
@esi_attempt.inner_html
|
62
|
+
rescue IncludeFailedError
|
63
|
+
context.process(@esi_except)
|
64
|
+
@esi_except.inner_html
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
class Choose < Node
|
70
|
+
def execute
|
71
|
+
whens = node.css('esi_when').to_a
|
72
|
+
raise "choose block contains no when elements" if whens.empty?
|
73
|
+
|
74
|
+
otherwise = node.css('esi_otherwise')[0]
|
75
|
+
|
76
|
+
whens.each do |esi_when|
|
77
|
+
if EsiAttributeLanguage::Grammar.parse(esi_when['test']).execute(context.resolver).equal?(true)
|
78
|
+
context.process(esi_when)
|
79
|
+
return esi_when.to_str
|
80
|
+
end
|
81
|
+
end
|
82
|
+
if otherwise
|
83
|
+
context.process(otherwise)
|
84
|
+
return otherwise.to_str
|
85
|
+
end
|
86
|
+
nil
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
class Context
|
91
|
+
|
92
|
+
attr_reader :resolver, :doc
|
93
|
+
|
94
|
+
def initialize(resolver, lookup)
|
95
|
+
@resolver = resolver
|
96
|
+
@lookup = lookup.is_a?(Array) ? lookup : [lookup]
|
97
|
+
@include = Include.new
|
98
|
+
@choose = Choose.new
|
99
|
+
@vars = Vars.new
|
100
|
+
@try = Try.new
|
101
|
+
|
102
|
+
end
|
103
|
+
|
104
|
+
def lookup(url)
|
105
|
+
@lookup.each do |l|
|
106
|
+
resolved_body = l[url]
|
107
|
+
return resolved_body if resolved_body
|
108
|
+
end
|
109
|
+
nil
|
110
|
+
end
|
111
|
+
|
112
|
+
def parse(document)
|
113
|
+
document.gsub!('esi:', 'esi_')
|
114
|
+
document.gsub!(/(<\/?esi_[^>]*>)/, ']]>\1<![CDATA[')
|
115
|
+
document[0,0] = %|<?xml version="1.0"?>\n<esi_root><![CDATA[|
|
116
|
+
document << ']]></esi_root>'
|
117
|
+
|
118
|
+
@doc = Nokogiri::XML(document)
|
119
|
+
|
120
|
+
@doc.css('esi_comment').each do |esi_comment|
|
121
|
+
esi_comment.replace(Nokogiri::XML::CDATA.new(doc, ''))
|
122
|
+
end
|
123
|
+
|
124
|
+
process(@doc.css('esi_root')[0])
|
125
|
+
|
126
|
+
result = ''
|
127
|
+
@doc.css('esi_root')[0].children.each do |n|
|
128
|
+
result << n.to_str
|
129
|
+
end
|
130
|
+
|
131
|
+
result
|
132
|
+
end
|
133
|
+
|
134
|
+
def process(doc_fragment)
|
135
|
+
# have to go one at a time because of limitation of .css, its not a live list.
|
136
|
+
# ps, i'll only break if i totally have to
|
137
|
+
loop do
|
138
|
+
should_break = true
|
139
|
+
doc_fragment.css('esi_try,esi_choose,esi_vars,esi_include').each do |esi_node|
|
140
|
+
case esi_node.name.to_sym
|
141
|
+
when :esi_include
|
142
|
+
@include.init(esi_node, self).execute_in_place!
|
143
|
+
when :esi_choose
|
144
|
+
@choose.init(esi_node, self).execute_in_place!
|
145
|
+
when :esi_vars
|
146
|
+
@vars.init(esi_node, self).execute_in_place!
|
147
|
+
when :esi_try
|
148
|
+
@try.init(esi_node, self).execute_in_place!
|
149
|
+
should_break = false
|
150
|
+
break
|
151
|
+
end
|
152
|
+
end
|
153
|
+
break if should_break
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
end
|
158
|
+
|
159
|
+
end
|
160
|
+
|
161
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
require 'spec/spec_helper'
|
2
|
+
|
3
|
+
describe "esi accept language lookups" do
|
4
|
+
|
5
|
+
it "should lookup a cookie variable" do
|
6
|
+
|
7
|
+
vars = {'type' => 'user'}
|
8
|
+
builder = Rack::Builder.new do
|
9
|
+
use EsiForRack, {'/file/WIN' => 'os', '/file/1.5' => 'version', '/file/MOZILLA' => 'browser'}
|
10
|
+
|
11
|
+
run proc { |env|
|
12
|
+
data = IO.read('spec/http_integration/fixtures/accept_language/1.html')
|
13
|
+
[200, {'Content-type' => 'text/html', 'Content-length' => data.size.to_s}, [data]]
|
14
|
+
}
|
15
|
+
end
|
16
|
+
|
17
|
+
request = Rack::MockRequest.env_for("/?#{Rack::Utils.build_query(vars)}")
|
18
|
+
request['HTTP_ACCEPT_LANGUAGE'] = 'da, en-gb;q=0.8, en;q=0.7'
|
19
|
+
builder.call(request).last.should == ["<html><body>\nEN\nDA\nEN-GB\n\n</body></html>"]
|
20
|
+
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
require 'spec/spec_helper'
|
2
|
+
|
3
|
+
describe "esi cookie variable lookups" do
|
4
|
+
|
5
|
+
it "should lookup a cookie variable" do
|
6
|
+
|
7
|
+
vars = {'type' => 'user'}
|
8
|
+
builder = Rack::Builder.new do
|
9
|
+
use EsiForRack, {'/file/1' => 'resource'}
|
10
|
+
|
11
|
+
run proc { |env|
|
12
|
+
data = IO.read('spec/http_integration/fixtures/cookie/1.html')
|
13
|
+
[200, {'Content-type' => 'text/html', 'Content-length' => data.size.to_s}, [data]]
|
14
|
+
}
|
15
|
+
end
|
16
|
+
|
17
|
+
request = Rack::MockRequest.env_for("/?#{Rack::Utils.build_query(vars)}")
|
18
|
+
request['HTTP_COOKIE'] = 'id=1'
|
19
|
+
builder.call(request).last.should == ["<html><body>\nresource\n</body></html>"]
|
20
|
+
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
<html><body>
|
2
|
+
<esi:choose>
|
3
|
+
<esi:when test="$(HTTP_ACCEPT_LANGUAGE{en})">EN</esi:when>
|
4
|
+
</esi:choose>
|
5
|
+
<esi:choose>
|
6
|
+
<esi:when test="$(HTTP_ACCEPT_LANGUAGE{da})">DA</esi:when>
|
7
|
+
</esi:choose>
|
8
|
+
<esi:choose>
|
9
|
+
<esi:when test="$(HTTP_ACCEPT_LANGUAGE{en-gb})">EN-GB</esi:when>
|
10
|
+
</esi:choose>
|
11
|
+
|
12
|
+
</body></html>
|
@@ -0,0 +1,22 @@
|
|
1
|
+
require 'spec/spec_helper'
|
2
|
+
|
3
|
+
describe "esi query string variable lookups" do
|
4
|
+
|
5
|
+
it "should lookup a query string variable" do
|
6
|
+
|
7
|
+
vars = {'id' => '1'}
|
8
|
+
builder = Rack::Builder.new do
|
9
|
+
use EsiForRack, {'/file/1' => 'resource'}
|
10
|
+
|
11
|
+
run proc { |env|
|
12
|
+
data = IO.read('spec/http_integration/fixtures/query_string/1.html')
|
13
|
+
[200, {'Content-type' => 'text/html', 'Content-length' => data.size.to_s}, [data]]
|
14
|
+
}
|
15
|
+
end
|
16
|
+
|
17
|
+
request = Rack::MockRequest.env_for("/?#{Rack::Utils.build_query(vars)}")
|
18
|
+
builder.call(request).last.should == ["<html><body>\nresource\n</body></html>"]
|
19
|
+
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
require 'spec/spec_helper'
|
2
|
+
|
3
|
+
describe "esi user agent variable lookups" do
|
4
|
+
|
5
|
+
it "should lookup a user agent variable" do
|
6
|
+
|
7
|
+
vars = {'type' => 'user'}
|
8
|
+
builder = Rack::Builder.new do
|
9
|
+
use EsiForRack, {'/file/WIN' => 'os', '/file/1.5' => 'version', '/file/MOZILLA' => 'browser'}
|
10
|
+
|
11
|
+
run proc { |env|
|
12
|
+
data = IO.read('spec/http_integration/fixtures/user_agent/1.html')
|
13
|
+
[200, {'Content-type' => 'text/html', 'Content-length' => data.size.to_s}, [data]]
|
14
|
+
}
|
15
|
+
end
|
16
|
+
|
17
|
+
request = Rack::MockRequest.env_for("/?#{Rack::Utils.build_query(vars)}")
|
18
|
+
request['HTTP_USER_AGENT'] = 'Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8.0) Gecko/20060308 Firefox/1.5.0'
|
19
|
+
builder.call(request).last.should == ["<html><body>\n browser\n os\n version\n</body></html>"]
|
20
|
+
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
data/spec/spec.opts
ADDED
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
require 'rack'
|
2
|
+
require 'lib/esi_for_rack'
|
3
|
+
|
4
|
+
def build_app(file, lookup)
|
5
|
+
builder = Rack::Builder.new do
|
6
|
+
use EsiForRack, lookup
|
7
|
+
|
8
|
+
run proc { |env|
|
9
|
+
data = if env['PATH_INFO'] == '/'
|
10
|
+
IO.read(file)
|
11
|
+
else
|
12
|
+
lookup[env['PATH_INFO']]
|
13
|
+
end
|
14
|
+
|
15
|
+
if data
|
16
|
+
[200, {'Content-type' => 'text/html', 'Content-length' => data.size.to_s}, [data]]
|
17
|
+
else
|
18
|
+
[404, {}, []]
|
19
|
+
end
|
20
|
+
}
|
21
|
+
end
|
22
|
+
|
23
|
+
request = Rack::MockRequest.env_for("/?a=b")
|
24
|
+
builder.call(request)
|
25
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
require 'spec/spec_helper'
|
2
|
+
|
3
|
+
describe "esi choose" do
|
4
|
+
|
5
|
+
it "should pick the first available choice" do
|
6
|
+
build_app('spec/tags/fixtures/choose/simple1.html', {}).last.should == ["\n Hey you\n "]
|
7
|
+
end
|
8
|
+
|
9
|
+
it "should pick next available choice" do
|
10
|
+
build_app('spec/tags/fixtures/choose/simple2.html', {}).last.should == ["\n And you\n "]
|
11
|
+
end
|
12
|
+
|
13
|
+
it "should take the otherwise block if there is no valid choice" do
|
14
|
+
build_app('spec/tags/fixtures/choose/simple3.html', {}).last.should == [" \n No good\n "]
|
15
|
+
end
|
16
|
+
|
17
|
+
|
18
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
This will <esi:comment>This will never show up</esi:comment>show up.
|
@@ -0,0 +1,12 @@
|
|
1
|
+
<esi:try>
|
2
|
+
<esi:attempt>
|
3
|
+
This is my include: <esi:include src="/include" />
|
4
|
+
Choice/<esi:choose>
|
5
|
+
<esi:when test="1==2">false</esi:when>
|
6
|
+
<esi:when test="1==1">true</esi:when>
|
7
|
+
</esi:choose>/Choice
|
8
|
+
</esi:attempt>
|
9
|
+
<esi:except>
|
10
|
+
BLAH!
|
11
|
+
</esi:except>
|
12
|
+
</esi:try>
|
@@ -0,0 +1 @@
|
|
1
|
+
<esi:vars><img src="http://www.example.com/$(QUERY_STRING{a})/hello.gif"/ ></esi:vars>
|
@@ -0,0 +1,25 @@
|
|
1
|
+
require 'spec/spec_helper'
|
2
|
+
|
3
|
+
describe "esi include" do
|
4
|
+
|
5
|
+
it "should include a src" do
|
6
|
+
build_app('spec/tags/fixtures/include/src.html', {'/great' => "<p>This is great</p>"}).last.should == ["<html><body>\n<p>This is great</p>\n</body></html>"]
|
7
|
+
end
|
8
|
+
|
9
|
+
it "should include an alt if src is unavilable" do
|
10
|
+
build_app('spec/tags/fixtures/include/alt.html', {'/alternate' => "<p>This is great</p>"}).last.should == ["<html><body>\n<p>This is great</p>\n</body></html>"]
|
11
|
+
end
|
12
|
+
|
13
|
+
it "should raise an error if src is unavilable" do
|
14
|
+
proc { build_app('spec/tags/fixtures/include/src.html', {}) }.should raise_error
|
15
|
+
end
|
16
|
+
|
17
|
+
it "should raise an error if src and alt is unavilable" do
|
18
|
+
proc { build_app('spec/tags/fixtures/include/alt.html', {}) }.should raise_error
|
19
|
+
end
|
20
|
+
|
21
|
+
it "should continue though, if onerror=continue" do
|
22
|
+
build_app('spec/tags/fixtures/include/src_continue.html', {}).last.should == ["<html><body>\n\n</body></html>"]
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
require 'spec/spec_helper'
|
2
|
+
|
3
|
+
describe "esi nested" do
|
4
|
+
|
5
|
+
it "nested1" do
|
6
|
+
build_app('spec/tags/fixtures/nested/complex.html', {}).last.should == ["\n BLAH!\n "]
|
7
|
+
end
|
8
|
+
|
9
|
+
it "nested2" do
|
10
|
+
build_app('spec/tags/fixtures/nested/complex.html', {'/include' => "<p>This is great</p>"}).last.should == ["\n This is my include: <p>This is great</p>\n Choice/true/Choice\n "]
|
11
|
+
end
|
12
|
+
|
13
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
require 'spec/spec_helper'
|
2
|
+
|
3
|
+
describe "esi try" do
|
4
|
+
|
5
|
+
it "should raise on try block without except" do
|
6
|
+
proc {
|
7
|
+
build_app('spec/tags/fixtures/try/malformed_attempt.html', {'/great' => "<p>This is great</p>"})
|
8
|
+
}.should raise_error
|
9
|
+
end
|
10
|
+
|
11
|
+
it "should raise on try block without attempt" do
|
12
|
+
proc {
|
13
|
+
build_app('spec/tags/fixtures/try/malformed_attempt.html', {'/great' => "<p>This is great</p>"})
|
14
|
+
}.should raise_error
|
15
|
+
end
|
16
|
+
|
17
|
+
it "should include normally within an attempt block" do
|
18
|
+
build_app('spec/tags/fixtures/try/include.html', {'/great' => "<p>This is great</p>"}).last.should ==
|
19
|
+
["\n <p>This is great</p>\n \n"]
|
20
|
+
end
|
21
|
+
|
22
|
+
it "should render the except block when the include fails within an attempt block" do
|
23
|
+
build_app('spec/tags/fixtures/try/include.html', {}).last.should ==
|
24
|
+
["This is bad\n"]
|
25
|
+
end
|
26
|
+
|
27
|
+
|
28
|
+
end
|
metadata
ADDED
@@ -0,0 +1,137 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: esi-for-rack
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.4
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Joshua Hull
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2009-11-25 00:00:00 -05:00
|
13
|
+
default_executable:
|
14
|
+
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
16
|
+
name: esi_attribute_language
|
17
|
+
type: :runtime
|
18
|
+
version_requirement:
|
19
|
+
version_requirements: !ruby/object:Gem::Requirement
|
20
|
+
requirements:
|
21
|
+
- - ">="
|
22
|
+
- !ruby/object:Gem::Version
|
23
|
+
version: "0"
|
24
|
+
version:
|
25
|
+
- !ruby/object:Gem::Dependency
|
26
|
+
name: nokogiri
|
27
|
+
type: :runtime
|
28
|
+
version_requirement:
|
29
|
+
version_requirements: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: "0"
|
34
|
+
version:
|
35
|
+
- !ruby/object:Gem::Dependency
|
36
|
+
name: spy-vs-spy
|
37
|
+
type: :runtime
|
38
|
+
version_requirement:
|
39
|
+
version_requirements: !ruby/object:Gem::Requirement
|
40
|
+
requirements:
|
41
|
+
- - ">="
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
version: "0"
|
44
|
+
version:
|
45
|
+
- !ruby/object:Gem::Dependency
|
46
|
+
name: dirge
|
47
|
+
type: :runtime
|
48
|
+
version_requirement:
|
49
|
+
version_requirements: !ruby/object:Gem::Requirement
|
50
|
+
requirements:
|
51
|
+
- - ">="
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: "0"
|
54
|
+
version:
|
55
|
+
description: ESI for Rack
|
56
|
+
email: joshbuddy@gmail.com
|
57
|
+
executables: []
|
58
|
+
|
59
|
+
extensions: []
|
60
|
+
|
61
|
+
extra_rdoc_files:
|
62
|
+
- README.rdoc
|
63
|
+
files:
|
64
|
+
- README.rdoc
|
65
|
+
- Rakefile
|
66
|
+
- VERSION
|
67
|
+
- lib/esi_for_rack.rb
|
68
|
+
- lib/esi_for_rack/lookup.rb
|
69
|
+
- lib/esi_for_rack/node.rb
|
70
|
+
- spec/http_integration/accept_language_spec.rb
|
71
|
+
- spec/http_integration/cookie_spec.rb
|
72
|
+
- spec/http_integration/fixtures/accept_language/1.html
|
73
|
+
- spec/http_integration/fixtures/cookie/1.html
|
74
|
+
- spec/http_integration/fixtures/query_string/1.html
|
75
|
+
- spec/http_integration/fixtures/user_agent/1.html
|
76
|
+
- spec/http_integration/query_string_spec.rb
|
77
|
+
- spec/http_integration/user_agent_spec.rb
|
78
|
+
- spec/spec.opts
|
79
|
+
- spec/spec_helper.rb
|
80
|
+
- spec/tags/choose_spec.rb
|
81
|
+
- spec/tags/comment_spec.rb
|
82
|
+
- spec/tags/fixtures/choose/simple1.html
|
83
|
+
- spec/tags/fixtures/choose/simple2.html
|
84
|
+
- spec/tags/fixtures/choose/simple3.html
|
85
|
+
- spec/tags/fixtures/comment/simple.html
|
86
|
+
- spec/tags/fixtures/include/alt.html
|
87
|
+
- spec/tags/fixtures/include/src.html
|
88
|
+
- spec/tags/fixtures/include/src_continue.html
|
89
|
+
- spec/tags/fixtures/nested/complex.html
|
90
|
+
- spec/tags/fixtures/try/include.html
|
91
|
+
- spec/tags/fixtures/try/malformed_attempt.html
|
92
|
+
- spec/tags/fixtures/try/malformed_except.html
|
93
|
+
- spec/tags/fixtures/vars/simple.html
|
94
|
+
- spec/tags/include_spec.rb
|
95
|
+
- spec/tags/nested_spec.rb
|
96
|
+
- spec/tags/try_spec.rb
|
97
|
+
- spec/tags/vars_spec.rb
|
98
|
+
has_rdoc: true
|
99
|
+
homepage: http://github.com/joshbuddy/esi_for_rack
|
100
|
+
licenses: []
|
101
|
+
|
102
|
+
post_install_message:
|
103
|
+
rdoc_options:
|
104
|
+
- --charset=UTF-8
|
105
|
+
require_paths:
|
106
|
+
- lib
|
107
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
108
|
+
requirements:
|
109
|
+
- - ">="
|
110
|
+
- !ruby/object:Gem::Version
|
111
|
+
version: "0"
|
112
|
+
version:
|
113
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
114
|
+
requirements:
|
115
|
+
- - ">="
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: "0"
|
118
|
+
version:
|
119
|
+
requirements: []
|
120
|
+
|
121
|
+
rubyforge_project:
|
122
|
+
rubygems_version: 1.3.5
|
123
|
+
signing_key:
|
124
|
+
specification_version: 3
|
125
|
+
summary: ESI for Rack
|
126
|
+
test_files:
|
127
|
+
- spec/http_integration/accept_language_spec.rb
|
128
|
+
- spec/http_integration/cookie_spec.rb
|
129
|
+
- spec/http_integration/query_string_spec.rb
|
130
|
+
- spec/http_integration/user_agent_spec.rb
|
131
|
+
- spec/spec_helper.rb
|
132
|
+
- spec/tags/choose_spec.rb
|
133
|
+
- spec/tags/comment_spec.rb
|
134
|
+
- spec/tags/include_spec.rb
|
135
|
+
- spec/tags/nested_spec.rb
|
136
|
+
- spec/tags/try_spec.rb
|
137
|
+
- spec/tags/vars_spec.rb
|