rack-esi 0.1.2 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/.gitignore CHANGED
@@ -1,21 +1,4 @@
1
- ## MAC OS
2
- .DS_Store
3
-
4
- ## TEXTMATE
5
- *.tmproj
6
- tmtags
7
-
8
- ## EMACS
9
- *~
10
- \#*
11
- .\#*
12
-
13
- ## VIM
14
- *.swp
15
-
16
- ## PROJECT::GENERAL
17
- coverage
18
- rdoc
19
- pkg
20
-
21
- ## PROJECT::SPECIFIC
1
+ *.gem
2
+ .bundle
3
+ Gemfile.lock
4
+ pkg/*
data/Gemfile ADDED
@@ -0,0 +1,2 @@
1
+ source "http://rubygems.org"
2
+ gemspec
data/LICENSE CHANGED
@@ -1,4 +1,4 @@
1
- Copyright (c) 2009 Florian Assmann
1
+ Copyright (c) 2009 Florian Aßmann
2
2
 
3
3
  Permission is hereby granted, free of charge, to any person obtaining
4
4
  a copy of this software and associated documentation files (the
@@ -0,0 +1,75 @@
1
+ # rack-esi
2
+
3
+ Rack-ESI is a Nokogiri based ESI middleware implementation for Rack with support for include tags, all other ESI namespaced nodes are just removed.
4
+
5
+ To make this gem work you must define the [xmlns:esi](http://www.edge-delivery.org/esi/1.0) namespace in your text/html response.
6
+
7
+ Note: This gem should only be used in development. For production use setup varnish or any other ESI enabled server.
8
+
9
+ ## Features
10
+
11
+ * threaded (in case we have slow IOs)
12
+ * PATH_INFO blacklisting (:skip => nil, should respond to ===)
13
+ * support for esi|include[alt] and esi|include[noerror] fallbacks
14
+
15
+ ## Dependencies
16
+
17
+ * Nokogiri
18
+ * Rack
19
+
20
+ ## Setup
21
+
22
+ ### w/o Gemfile
23
+
24
+ $ gem install rack-esi
25
+
26
+ ### w/ Gemfile
27
+
28
+ gem 'rack-esi'
29
+
30
+ ### rackup
31
+
32
+ use Rack::ESI, options || {}
33
+ run Application.new
34
+
35
+ ### Rails 2.3: environment.rb
36
+
37
+ config.gem 'rack-esi' # for setups w/o Gemfile
38
+ config.middleware.unshift Rack::ESI
39
+
40
+ ### Rails 3.1: application.rb
41
+
42
+ config.middleware.insert_before ActionDispatch::Static, Rack::ESI
43
+
44
+ ## Options
45
+
46
+ * poolsize: 4,
47
+ Number of worker threads. A value of 1 disables threading model.
48
+ * skip: nil,
49
+ This should be an object which responds to #===(PATH_INFO).
50
+ * parser: Nokogiri::XML::Document,
51
+ You can change this to Nokogiri::HTML::Document, but you should change the serializer, too (see below).
52
+ * serializer: :to_xhtml,
53
+ The serializer value specifies the method name which is send to the object created by the parser#parse.
54
+
55
+ ## TODO
56
+
57
+ * write documentation
58
+ * write more tests
59
+ * support more ESI elements
60
+
61
+ ## Note on Patches/Pull Requests
62
+
63
+ * Fork the project.
64
+ * Make your feature addition or bug fix.
65
+ * Add tests for it.
66
+ * Commit, do not mess with rakefile, version, or history.
67
+ * Send me a pull request.
68
+
69
+ ## Thanks
70
+
71
+ tenderlove and Qerub
72
+
73
+ ## Copyright
74
+
75
+ Copyright (c) 2009 Florian Aßmann. See LICENSE for details.
data/Rakefile CHANGED
@@ -1,53 +1 @@
1
- require 'rubygems'
2
- require 'rake'
3
-
4
- begin
5
- require 'jeweler'
6
- Jeweler::Tasks.new do |gem|
7
- gem.name = "rack-esi"
8
- gem.summary = %Q{ESI middleware implementation for Rack.}
9
- gem.description = %Q{Nokogiri based ESI middleware implementation for Rack with (limited) support for include, remove and comment.}
10
- gem.email = "florian.assmann@email.de"
11
- gem.homepage = "http://github.com/boof/rack-esi"
12
- gem.authors = ["Florian Aßmann"]
13
- gem.add_development_dependency "riot", ">= 0"
14
- gem.add_development_dependency "yard", ">= 0"
15
- gem.add_dependency 'nokogiri', '>= 0'
16
- end
17
- Jeweler::GemcutterTasks.new
18
- rescue LoadError
19
- puts "Jeweler (or a dependency) not available. Install it with: gem install jeweler"
20
- end
21
-
22
- require 'rake/testtask'
23
- Rake::TestTask.new(:test) do |test|
24
- test.libs << 'lib' << 'test'
25
- test.pattern = 'test/**/*_test.rb'
26
- test.verbose = true
27
- end
28
-
29
- begin
30
- require 'rcov/rcovtask'
31
- Rcov::RcovTask.new do |test|
32
- test.libs << 'test'
33
- test.pattern = 'test/**/*_test.rb'
34
- test.verbose = true
35
- end
36
- rescue LoadError
37
- task :rcov do
38
- abort "RCov is not available. In order to run rcov, you must: sudo gem install spicycode-rcov"
39
- end
40
- end
41
-
42
- task :test => :check_dependencies
43
-
44
- task :default => :test
45
-
46
- begin
47
- require 'yard'
48
- YARD::Rake::YardocTask.new
49
- rescue LoadError
50
- task :yardoc do
51
- abort "YARD is not available. In order to run yardoc, you must: sudo gem install yard"
52
- end
53
- end
1
+ require "bundler/gem_tasks"
@@ -1,97 +1,45 @@
1
1
  require 'rack'
2
2
  require 'nokogiri'
3
+ require 'bundler'
4
+ Bundler.require
3
5
 
4
- class Rack::ESI
5
- NS = { 'esi' => 'http://www.edge-delivery.org/esi/1.0' }
6
- METHODS = { 'include' => :esi_include, 'remove' => nil, 'comment' => nil }
7
- CSS = METHODS.keys.map { |cmd| "esi|#{ cmd }" } * ','
6
+ require File.expand_path('../rack-esi/processor', __FILE__)
8
7
 
9
- class Error < RuntimeError
10
- def initialize(status, headers, response)
11
- @status, @headers, @response = status, headers, response
12
- end
13
- def finish
14
- return [@status, @headers, backtrace]
15
- end
16
- end
8
+ class Rack::ESI
17
9
 
18
10
  def initialize(app, options = {})
19
- @app = app
11
+ @app = app
20
12
 
21
- @paths = options[:skip]
22
- @types = options[:only] || /^text\/(?:x|ht)ml/
23
- @max_includes = options[:includes] || 32
24
- @max_recursion = options[:depth] || 5
13
+ @parser = options.fetch :parser, Nokogiri::XML::Document
14
+ @serializer = options.fetch :serializer, :to_xhtml
15
+ @skip = options[:skip]
16
+ @poolsize = options.fetch :poolsize, 4
17
+ @processor = @poolsize == 1 ? Processor::Linear : Processor::Threaded
25
18
  end
26
19
 
27
- def call env, counter = { :recursion => 0, :includes => 0 }
28
- return @app.call(env) if skip_path? env['PATH_INFO']
29
-
30
- status, headers, input = @app.call env.dup
31
- return status, headers, input if skip_type? headers['Content-Type']
32
-
33
- output = []
34
- input.each { |body| output << compile_body(body, env, counter) }
35
-
36
- Rack::Response.new(output, status, headers).finish
37
- end
38
-
39
- private
40
-
41
- def with_compiled_path(env, path)
42
- # TODO: should compile variables.
43
- env.merge 'PATH_INFO' => path, 'REQUEST_URI' => path
44
- end
20
+ def queue(&block)
21
+ unless @queue
22
+ @queue, @group = Queue.new, ThreadGroup.new
23
+ @poolsize.times { @group.add Worker.new(@queue) }
45
24
 
46
- def fetch(path, env, counter)
47
- call with_compiled_path(env, path), counter if path
48
- rescue => e
49
- return [500, {}, e.backtrace]
25
+ at_exit { Finisher.wait @queue }
50
26
  end
51
27
 
52
- # Should I use XML::SAX::Parser?
53
- def compile_body(body, env, counter)
54
- document = Nokogiri.XML body
28
+ @queue.push block
29
+ end
55
30
 
56
- document.css(CSS, NS).each do |node|
57
- method = METHODS[node.name] and send method, node, env, counter
58
- node.unlink
59
- end
31
+ attr_reader :parser, :serializer
60
32
 
61
- document.to_xhtml
62
- end
33
+ def call(env)
34
+ return @app.call(env) if @skip === env['PATH_INFO']
63
35
 
64
- def skip_path?(path)
65
- @paths =~ path if @paths
66
- end
67
- def skip_type?(type)
68
- @types !~ type
69
- end
36
+ status, headers, body = @app.call env.dup
70
37
 
71
- def max?(counter)
72
- not counter[:includes] < @max_includes &&
73
- counter[:recursion] < @max_recursion
38
+ if status == 200 and headers['Content-Type'] =~ /text\/html/
39
+ body = @processor.new(self, env).process body
74
40
  end
75
41
 
76
- def esi_include(node, env, counter)
77
- return if max? counter
78
-
79
- counter[:includes] += 1
80
- counter[:recursion] += 1
81
-
82
- status, headers, response = fetch node['src'], env, counter
83
- status, headers, response = fetch node['alt'], env, counter if status != 200
84
-
85
- if status == 200
86
- data = ''
87
- response.each { |inc| data << inc }
88
- node.before data
89
- elsif node['onerror'] != 'continue'
90
- raise Error.new(status, headers, response)
91
- end
92
-
93
- ensure
94
- counter[:recursion] -= 1
95
- end
42
+ return status, headers, body
43
+ end
96
44
 
97
45
  end
@@ -0,0 +1,55 @@
1
+ class Rack::ESI
2
+ class Processor < Struct.new(:esi, :env)
3
+
4
+ class Linear < self
5
+ def process_document(d)
6
+ d.xpath('//e:*', 'e' => NAMESPACE).each { |n| process_node n }
7
+ end
8
+ end
9
+ autoload :Threaded, File.expand_path('../threaded', __FILE__)
10
+
11
+ NAMESPACE = 'http://www.edge-delivery.org/esi/1.0'
12
+ Error = Class.new RuntimeError
13
+
14
+ def read(enumerable, buffer = '')
15
+ enumerable.each { |str| buffer << str }
16
+ buffer
17
+ end
18
+
19
+ def include(path)
20
+ # RADAR patron here?
21
+ esi.call env.merge('PATH_INFO' => path, 'REQUEST_URI' => path)
22
+ rescue => e
23
+ return 500, {}, []
24
+ end
25
+ def process_node(node)
26
+ case node.name
27
+ when 'include'
28
+ status, headers, body = include node['src']
29
+
30
+ unless status == 200 or node['alt'].nil?
31
+ status, headers, body = include node['alt']
32
+ end
33
+
34
+ if status == 200
35
+ node.replace read(body)
36
+ elsif node['onerror'] != 'continue'
37
+ raise Error
38
+ end
39
+ else
40
+ node.remove
41
+ end
42
+ end
43
+ def process_document(document)
44
+ raise NotImplementedError
45
+ end
46
+ def process(body)
47
+ document = esi.parser.parse read(body)
48
+ process_document document
49
+ [
50
+ document.send( esi.serializer )
51
+ ]
52
+ end
53
+
54
+ end
55
+ end
@@ -0,0 +1,50 @@
1
+ require 'thread'
2
+ #require 'timeout'
3
+
4
+ class Rack::ESI
5
+
6
+ class Finisher < Proc
7
+ def self.wait(queue)
8
+ finisher = new do |worker|
9
+ puts "Finishing #{ worker.inspect }..."
10
+ worker[:finish] = true
11
+ queue.push finisher
12
+ end
13
+
14
+ # cast the first stone
15
+ queue.push finisher
16
+
17
+ # wait at the end
18
+ queue.pop
19
+ end
20
+ end
21
+
22
+ class Worker < Thread
23
+ def initialize(queue)
24
+ super do
25
+ begin
26
+ queue.pop[ self ]
27
+ rescue => e
28
+ puts e
29
+ end until key? :finish
30
+ end
31
+ end
32
+ end
33
+
34
+ class Processor::Threaded < Processor
35
+ def process_document(document)
36
+ nodes = document.xpath '//e:*', 'e' => NAMESPACE
37
+
38
+ countdown, main = nodes.length, Thread.current
39
+ nodes.each do |node|
40
+ esi.queue do
41
+ process_node node
42
+ main.run if (countdown -= 1).zero?
43
+ end
44
+ end
45
+ # TODO prevent nesting depth bigger than poolsize
46
+ Thread.stop if countdown > 0 # wait for worker
47
+ end
48
+ end
49
+
50
+ end
@@ -0,0 +1,5 @@
1
+ module Rack
2
+ class ESI
3
+ VERSION = "0.2.0"
4
+ end
5
+ end
@@ -0,0 +1,28 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "rack-esi/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "rack-esi"
7
+ s.version = Rack::ESI::VERSION
8
+ s.authors = ["Florian Aßmann"]
9
+ s.email = ["florian.assmann@email.de"]
10
+ s.homepage = ""
11
+ s.summary = %q{ ESI middleware implementation for Rack. }
12
+ s.description = <<-EOF
13
+ Rack-ESI is a Nokogiri based ESI middleware implementation for Rack with support for include tags, all other ESI namespaced nodes are just removed.
14
+ To make this gem work you must define the (xmlns:esi)[http://www.edge-delivery.org/esi/1.0] namespace in your text/html response.
15
+ Note: This gem should only be used in development. For production use setup varnish or any other ESI enabled server.
16
+ EOF
17
+ # s.rubyforge_project = "rack-esi"
18
+
19
+ s.files = `git ls-files`.split("\n")
20
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
21
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
22
+ s.require_paths = ["lib"]
23
+
24
+ s.add_dependency "rack"
25
+ s.add_dependency "nokogiri"
26
+ # s.add_dependency "patron"
27
+ s.add_development_dependency "riot"
28
+ end
@@ -0,0 +1,31 @@
1
+ require File.expand_path('../teststrap', __FILE__)
2
+
3
+ context 'Rack::ESI' do
4
+
5
+ __dirname__ = File.expand_path File.dirname(__FILE__)
6
+ root = Pathname.new File.join(__dirname__, 'fixtures')
7
+ opts = { :urls => ['/'], :root => root }
8
+
9
+ setup { ESI.new Static.new(App.new, opts), skip: /raw/, :poolsize => 1 }
10
+
11
+ context 'GET /raw.html' do
12
+ setup { MockRequest.new(topic).get '/raw.html' }
13
+ asserts('Content-Type') { topic.content_type }.equals 'text/html'
14
+ should('not be altered') { topic.body == root.join('raw.html').read }
15
+ end
16
+
17
+ context 'GET /index.html' do
18
+ setup { MockRequest.new(topic).get '/index.html' }
19
+
20
+ asserts('Content-Type') { topic.content_type }.equals 'text/html'
21
+ should('not have any ESI specific nodes') do
22
+ html(topic.body).
23
+ at('//e:*', 'e' => Rack::ESI::Processor::NAMESPACE).nil?
24
+ end
25
+ should('have meta replacement with content') do
26
+ not html(topic.body).
27
+ at("//meta[@name='replacement' and @content='content']").nil?
28
+ end
29
+ end
30
+
31
+ end
@@ -1,4 +1,4 @@
1
- require 'teststrap'
1
+ require File.expand_path('../teststrap', __FILE__)
2
2
 
3
3
  context 'Rack::ESI' do
4
4
 
@@ -6,7 +6,7 @@ context 'Rack::ESI' do
6
6
  root = Pathname.new File.join(__dirname__, 'fixtures')
7
7
  opts = { :urls => ['/'], :root => root }
8
8
 
9
- setup { ESI.new Static.new(App.new, opts), :skip => /raw/ }
9
+ setup { ESI.new Static.new(App.new, opts), skip: /raw/, :poolsize => 1 }
10
10
 
11
11
  context 'GET /raw.html' do
12
12
  setup { MockRequest.new(topic).get '/raw.html' }
@@ -20,7 +20,7 @@ context 'Rack::ESI' do
20
20
  asserts('Content-Type') { topic.content_type }.equals 'text/html'
21
21
  should('not have any ESI specific nodes') do
22
22
  html(topic.body).
23
- at('//esi:include|//esi:remove|//esi:comment', Rack::ESI::NS).nil?
23
+ at('//e:*', 'e' => Rack::ESI::Processor::NAMESPACE).nil?
24
24
  end
25
25
  should('have meta replacement with content') do
26
26
  not html(topic.body).
@@ -1,8 +1,12 @@
1
1
  require 'pathname'
2
- require 'rubygems'
3
- require 'riot'
4
- require 'rack-esi'
5
2
  require 'rack/mock'
3
+ require 'rack/static'
4
+ require 'rack/file'
5
+
6
+ require File.expand_path('../../lib/rack-esi', __FILE__)
7
+ Bundler.require :development
8
+
9
+ Nokogiri
6
10
 
7
11
  def html(body)
8
12
  Nokogiri.HTML(body).root
metadata CHANGED
@@ -1,97 +1,111 @@
1
- --- !ruby/object:Gem::Specification
1
+ --- !ruby/object:Gem::Specification
2
2
  name: rack-esi
3
- version: !ruby/object:Gem::Version
4
- version: 0.1.2
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.2.0
5
+ prerelease:
5
6
  platform: ruby
6
- authors:
7
- - "Florian A\xC3\x9Fmann"
7
+ authors:
8
+ - Florian Aßmann
8
9
  autorequire:
9
10
  bindir: bin
10
11
  cert_chain: []
11
-
12
- date: 2009-11-27 00:00:00 +01:00
13
- default_executable:
14
- dependencies:
15
- - !ruby/object:Gem::Dependency
16
- name: riot
17
- type: :development
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: yard
27
- type: :development
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
12
+ date: 2011-09-30 00:00:00.000000000Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: rack
16
+ requirement: &70252059616420 !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: *70252059616420
25
+ - !ruby/object:Gem::Dependency
36
26
  name: nokogiri
27
+ requirement: &70252059615980 !ruby/object:Gem::Requirement
28
+ none: false
29
+ requirements:
30
+ - - ! '>='
31
+ - !ruby/object:Gem::Version
32
+ version: '0'
37
33
  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
- description: Nokogiri based ESI middleware implementation for Rack with (limited) support for include, remove and comment.
46
- email: florian.assmann@email.de
47
- executables: []
34
+ prerelease: false
35
+ version_requirements: *70252059615980
36
+ - !ruby/object:Gem::Dependency
37
+ name: riot
38
+ requirement: &70252059615560 !ruby/object:Gem::Requirement
39
+ none: false
40
+ requirements:
41
+ - - ! '>='
42
+ - !ruby/object:Gem::Version
43
+ version: '0'
44
+ type: :development
45
+ prerelease: false
46
+ version_requirements: *70252059615560
47
+ description: ! 'Rack-ESI is a Nokogiri based ESI middleware implementation for Rack
48
+ with support for include tags, all other ESI namespaced nodes are just removed.
48
49
 
49
- extensions: []
50
+ To make this gem work you must define the (xmlns:esi)[http://www.edge-delivery.org/esi/1.0]
51
+ namespace in your text/html response.
50
52
 
51
- extra_rdoc_files:
52
- - LICENSE
53
- - README.markdown
54
- files:
53
+ Note: This gem should only be used in development. For production use setup varnish
54
+ or any other ESI enabled server.
55
+
56
+ '
57
+ email:
58
+ - florian.assmann@email.de
59
+ executables: []
60
+ extensions: []
61
+ extra_rdoc_files: []
62
+ files:
55
63
  - .document
56
64
  - .gitignore
65
+ - Gemfile
66
+ - Gemfile.lock
57
67
  - LICENSE
58
- - README.markdown
68
+ - README.md
59
69
  - Rakefile
60
- - VERSION
61
70
  - lib/rack-esi.rb
71
+ - lib/rack-esi/processor.rb
72
+ - lib/rack-esi/threaded.rb
73
+ - lib/rack-esi/version.rb
74
+ - rack-esi.gemspec
75
+ - test/_test.rb
62
76
  - test/fixtures/index.html
63
77
  - test/fixtures/metatags.fragment
64
78
  - test/fixtures/raw.html
65
79
  - test/rack-esi_test.rb
66
80
  - test/teststrap.rb
67
- has_rdoc: true
68
- homepage: http://github.com/boof/rack-esi
81
+ homepage: ''
69
82
  licenses: []
70
-
71
83
  post_install_message:
72
- rdoc_options:
73
- - --charset=UTF-8
74
- require_paths:
84
+ rdoc_options: []
85
+ require_paths:
75
86
  - lib
76
- required_ruby_version: !ruby/object:Gem::Requirement
77
- requirements:
78
- - - ">="
79
- - !ruby/object:Gem::Version
80
- version: "0"
81
- version:
82
- required_rubygems_version: !ruby/object:Gem::Requirement
83
- requirements:
84
- - - ">="
85
- - !ruby/object:Gem::Version
86
- version: "0"
87
- version:
87
+ required_ruby_version: !ruby/object:Gem::Requirement
88
+ none: false
89
+ requirements:
90
+ - - ! '>='
91
+ - !ruby/object:Gem::Version
92
+ version: '0'
93
+ required_rubygems_version: !ruby/object:Gem::Requirement
94
+ none: false
95
+ requirements:
96
+ - - ! '>='
97
+ - !ruby/object:Gem::Version
98
+ version: '0'
88
99
  requirements: []
89
-
90
100
  rubyforge_project:
91
- rubygems_version: 1.3.5
101
+ rubygems_version: 1.8.8
92
102
  signing_key:
93
103
  specification_version: 3
94
104
  summary: ESI middleware implementation for Rack.
95
- test_files:
105
+ test_files:
106
+ - test/_test.rb
107
+ - test/fixtures/index.html
108
+ - test/fixtures/metatags.fragment
109
+ - test/fixtures/raw.html
96
110
  - test/rack-esi_test.rb
97
111
  - test/teststrap.rb
@@ -1,54 +0,0 @@
1
- # rack-esi
2
-
3
- Nokogiri based ESI middleware implementation for Rack with (limited) support
4
- for include, remove and comment.
5
-
6
- ## Features
7
-
8
- * path blacklisting (:skip => nil, expects Regexp)
9
- * type whitelisting (:only => /^text\/(?:x|ht)ml/)
10
- * recursion limit (:depth => 5)
11
- * include limits (:includes => 32)
12
- * support for &lt;include&gt; alt and noerror attributes
13
-
14
- _It's for development purpose..._
15
-
16
- ## Installation
17
-
18
- gem install rack-esi
19
-
20
- ## Rails Setup (environment.rb)
21
-
22
- config.gem 'rack-esi'
23
- require 'rack-esi'
24
- config.middleware.insert_before config.middleware.first, Rack::ESI
25
-
26
- ## TODO
27
-
28
- * write documentation
29
- * write more tests
30
- * support more ESI elements
31
- * switch to Nokogiri::XML::SAX::Document?
32
-
33
- ## Dependencies
34
-
35
- * Nokogiri
36
- * Rack
37
-
38
- ## Note on Patches/Pull Requests
39
-
40
- * Fork the project.
41
- * Make your feature addition or bug fix.
42
- * Add tests for it. This is important so I don't break it in a
43
- future version unintentionally.
44
- * Commit, do not mess with rakefile, version, or history.
45
- (if you want to have your own version, that is fine but bump version in a commit by itself I can ignore when I pull)
46
- * Send me a pull request. Bonus points for topic branches.
47
-
48
- ## Thanks
49
-
50
- tenderlove and Qerub
51
-
52
- ## Copyright
53
-
54
- Copyright (c) 2009 Florian Assmann. See LICENSE for details.
data/VERSION DELETED
@@ -1 +0,0 @@
1
- 0.1.2