esi-for-rack 0.0.4
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/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
|