rack_dav 0.1.3 → 0.3.1
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 +2 -0
- data/CHANGELOG.md +27 -0
- data/Gemfile +4 -0
- data/Gemfile.lock +31 -0
- data/README.md +14 -15
- data/Rakefile +36 -0
- data/bin/rack_dav +11 -7
- data/lib/rack_dav.rb +1 -3
- data/lib/rack_dav/controller.rb +300 -218
- data/lib/rack_dav/file_resource.rb +48 -32
- data/lib/rack_dav/handler.rb +19 -12
- data/lib/rack_dav/http_status.rb +10 -10
- data/lib/rack_dav/resource.rb +21 -17
- data/lib/rack_dav/string.rb +28 -0
- data/lib/rack_dav/version.rb +16 -0
- data/rack_dav.gemspec +22 -26
- data/spec/controller_spec.rb +498 -0
- data/spec/file_resource_spec.rb +11 -0
- data/spec/fixtures/folder/01.txt +0 -0
- data/spec/fixtures/folder/02.txt +0 -0
- data/spec/fixtures/requests/lock.xml +12 -0
- data/spec/handler_spec.rb +23 -256
- data/spec/spec_helper.rb +27 -0
- data/spec/support/lockable_file_resource.rb +30 -0
- metadata +107 -50
- data/lib/rack_dav/builder_namespace.rb +0 -22
File without changes
|
File without changes
|
data/spec/handler_spec.rb
CHANGED
@@ -1,270 +1,37 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
require 'rubygems'
|
4
|
-
require 'rack_dav'
|
5
|
-
require 'fileutils'
|
1
|
+
require 'spec_helper'
|
6
2
|
|
7
3
|
describe RackDAV::Handler do
|
8
|
-
DOC_ROOT = File.expand_path(File.dirname(__FILE__) + '/htdocs')
|
9
|
-
METHODS = %w(GET PUT POST DELETE PROPFIND PROPPATCH MKCOL COPY MOVE OPTIONS HEAD LOCK UNLOCK)
|
10
|
-
|
11
|
-
before do
|
12
|
-
FileUtils.mkdir(DOC_ROOT) unless File.exists?(DOC_ROOT)
|
13
|
-
@controller = RackDAV::Handler.new(:root => DOC_ROOT)
|
14
|
-
end
|
15
|
-
|
16
|
-
after do
|
17
|
-
FileUtils.rm_rf(DOC_ROOT) if File.exists?(DOC_ROOT)
|
18
|
-
end
|
19
|
-
|
20
|
-
attr_reader :response
|
21
|
-
|
22
|
-
def request(method, uri, options={})
|
23
|
-
options = {
|
24
|
-
'HTTP_HOST' => 'localhost',
|
25
|
-
'REMOTE_USER' => 'manni'
|
26
|
-
}.merge(options)
|
27
|
-
request = Rack::MockRequest.new(@controller)
|
28
|
-
@response = request.request(method, uri, options)
|
29
|
-
end
|
30
|
-
|
31
|
-
METHODS.each do |method|
|
32
|
-
define_method(method.downcase) do |*args|
|
33
|
-
request(method, *args)
|
34
|
-
end
|
35
|
-
end
|
36
|
-
|
37
|
-
def render
|
38
|
-
xml = Builder::XmlMarkup.new
|
39
|
-
xml.instruct! :xml, :version => "1.0", :encoding => "UTF-8"
|
40
|
-
xml.namespace('d') do
|
41
|
-
yield xml
|
42
|
-
end
|
43
|
-
xml.target!
|
44
|
-
end
|
45
|
-
|
46
|
-
def url_escape(string)
|
47
|
-
string.gsub(/([^ a-zA-Z0-9_.-]+)/n) do
|
48
|
-
'%' + $1.unpack('H2' * $1.size).join('%').upcase
|
49
|
-
end.tr(' ', '+')
|
50
|
-
end
|
51
|
-
|
52
|
-
def response_xml
|
53
|
-
REXML::Document.new(@response.body)
|
54
|
-
end
|
55
|
-
|
56
|
-
def multistatus_response(pattern)
|
57
|
-
@response.should be_multi_status
|
58
|
-
REXML::XPath::match(response_xml, "/multistatus/response", '' => 'DAV:').should_not be_empty
|
59
|
-
REXML::XPath::match(response_xml, "/multistatus/response" + pattern, '' => 'DAV:')
|
60
|
-
end
|
61
4
|
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
xml.tag! prop
|
68
|
-
end
|
69
|
-
end
|
70
|
-
end
|
71
|
-
end
|
72
|
-
end
|
73
|
-
|
74
|
-
it 'should return all options' do
|
75
|
-
options('/').should be_ok
|
76
|
-
|
77
|
-
METHODS.each do |method|
|
78
|
-
response.headers['allow'].should include(method)
|
5
|
+
describe "#initialize" do
|
6
|
+
it "accepts zero parameters" do
|
7
|
+
lambda do
|
8
|
+
klass.new
|
9
|
+
end.should_not raise_error
|
79
10
|
end
|
80
|
-
end
|
81
|
-
|
82
|
-
it 'should return headers' do
|
83
|
-
put('/test.html', :input => '<html/>').should be_ok
|
84
|
-
head('/test.html').should be_ok
|
85
|
-
|
86
|
-
response.headers['etag'].should_not be_nil
|
87
|
-
response.headers['content-type'].should match(/html/)
|
88
|
-
response.headers['last-modified'].should_not be_nil
|
89
|
-
end
|
90
|
-
|
91
|
-
it 'should not find a nonexistent resource' do
|
92
|
-
get('/not_found').should be_not_found
|
93
|
-
end
|
94
|
-
|
95
|
-
it 'should not allow directory traversal' do
|
96
|
-
get('/../htdocs').should be_forbidden
|
97
|
-
end
|
98
|
-
|
99
|
-
it 'should create a resource and allow its retrieval' do
|
100
|
-
put('/test', :input => 'body').should be_ok
|
101
|
-
get('/test').should be_ok
|
102
|
-
response.body.should == 'body'
|
103
|
-
end
|
104
|
-
it 'should create and find a url with escaped characters' do
|
105
|
-
put(url_escape('/a b'), :input => 'body').should be_ok
|
106
|
-
get(url_escape('/a b')).should be_ok
|
107
|
-
response.body.should == 'body'
|
108
|
-
end
|
109
|
-
|
110
|
-
it 'should delete a single resource' do
|
111
|
-
put('/test', :input => 'body').should be_ok
|
112
|
-
delete('/test').should be_no_content
|
113
|
-
end
|
114
|
-
|
115
|
-
it 'should delete recursively' do
|
116
|
-
mkcol('/folder').should be_created
|
117
|
-
put('/folder/a', :input => 'body').should be_ok
|
118
|
-
put('/folder/b', :input => 'body').should be_ok
|
119
|
-
|
120
|
-
delete('/folder').should be_no_content
|
121
|
-
get('/folder').should be_not_found
|
122
|
-
get('/folder/a').should be_not_found
|
123
|
-
get('/folder/b').should be_not_found
|
124
|
-
end
|
125
11
|
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
it 'should not allow copy to the same resource' do
|
132
|
-
put('/test', :input => 'body').should be_ok
|
133
|
-
copy('/test', 'HTTP_DESTINATION' => '/test').should be_forbidden
|
134
|
-
end
|
135
|
-
|
136
|
-
it 'should not allow an invalid destination uri' do
|
137
|
-
put('/test', :input => 'body').should be_ok
|
138
|
-
copy('/test', 'HTTP_DESTINATION' => '%').should be_bad_request
|
139
|
-
end
|
140
|
-
|
141
|
-
it 'should copy a single resource' do
|
142
|
-
put('/test', :input => 'body').should be_ok
|
143
|
-
copy('/test', 'HTTP_DESTINATION' => '/copy').should be_created
|
144
|
-
get('/copy').body.should == 'body'
|
145
|
-
end
|
146
|
-
|
147
|
-
it 'should copy a resource with escaped characters' do
|
148
|
-
put(url_escape('/a b'), :input => 'body').should be_ok
|
149
|
-
copy(url_escape('/a b'), 'HTTP_DESTINATION' => url_escape('/a c')).should be_created
|
150
|
-
get(url_escape('/a c')).should be_ok
|
151
|
-
response.body.should == 'body'
|
152
|
-
end
|
153
|
-
|
154
|
-
it 'should deny a copy without overwrite' do
|
155
|
-
put('/test', :input => 'body').should be_ok
|
156
|
-
put('/copy', :input => 'copy').should be_ok
|
157
|
-
copy('/test', 'HTTP_DESTINATION' => '/copy', 'HTTP_OVERWRITE' => 'F')
|
158
|
-
|
159
|
-
multistatus_response('/href').first.text.should == 'http://localhost/test'
|
160
|
-
multistatus_response('/status').first.text.should match(/412 Precondition Failed/)
|
161
|
-
|
162
|
-
get('/copy').body.should == 'copy'
|
163
|
-
end
|
164
|
-
|
165
|
-
it 'should allow a copy with overwrite' do
|
166
|
-
put('/test', :input => 'body').should be_ok
|
167
|
-
put('/copy', :input => 'copy').should be_ok
|
168
|
-
copy('/test', 'HTTP_DESTINATION' => '/copy', 'HTTP_OVERWRITE' => 'T').should be_no_content
|
169
|
-
get('/copy').body.should == 'body'
|
170
|
-
end
|
171
|
-
|
172
|
-
it 'should copy a collection' do
|
173
|
-
mkcol('/folder').should be_created
|
174
|
-
copy('/folder', 'HTTP_DESTINATION' => '/copy').should be_created
|
175
|
-
propfind('/copy', :input => propfind_xml(:resourcetype))
|
176
|
-
multistatus_response('/propstat/prop/resourcetype/collection').should_not be_empty
|
177
|
-
end
|
178
|
-
|
179
|
-
it 'should copy a collection resursively' do
|
180
|
-
mkcol('/folder').should be_created
|
181
|
-
put('/folder/a', :input => 'A').should be_ok
|
182
|
-
put('/folder/b', :input => 'B').should be_ok
|
183
|
-
|
184
|
-
copy('/folder', 'HTTP_DESTINATION' => '/copy').should be_created
|
185
|
-
propfind('/copy', :input => propfind_xml(:resourcetype))
|
186
|
-
multistatus_response('/propstat/prop/resourcetype/collection').should_not be_empty
|
187
|
-
|
188
|
-
get('/copy/a').body.should == 'A'
|
189
|
-
get('/copy/b').body.should == 'B'
|
190
|
-
end
|
191
|
-
|
192
|
-
it 'should move a collection recursively' do
|
193
|
-
mkcol('/folder').should be_created
|
194
|
-
put('/folder/a', :input => 'A').should be_ok
|
195
|
-
put('/folder/b', :input => 'B').should be_ok
|
196
|
-
|
197
|
-
move('/folder', 'HTTP_DESTINATION' => '/move').should be_created
|
198
|
-
propfind('/move', :input => propfind_xml(:resourcetype))
|
199
|
-
multistatus_response('/propstat/prop/resourcetype/collection').should_not be_empty
|
200
|
-
|
201
|
-
get('/move/a').body.should == 'A'
|
202
|
-
get('/move/b').body.should == 'B'
|
203
|
-
get('/folder/a').should be_not_found
|
204
|
-
get('/folder/b').should be_not_found
|
205
|
-
end
|
206
|
-
|
207
|
-
it 'should create a collection' do
|
208
|
-
mkcol('/folder').should be_created
|
209
|
-
propfind('/folder', :input => propfind_xml(:resourcetype))
|
210
|
-
multistatus_response('/propstat/prop/resourcetype/collection').should_not be_empty
|
211
|
-
end
|
212
|
-
|
213
|
-
it 'should not find properties for nonexistent resources' do
|
214
|
-
propfind('/non').should be_not_found
|
215
|
-
end
|
216
|
-
|
217
|
-
it 'should find all properties' do
|
218
|
-
xml = render do |xml|
|
219
|
-
xml.propfind('xmlns:d' => "DAV:") do
|
220
|
-
xml.allprop
|
221
|
-
end
|
12
|
+
it "accepts a hash of options" do
|
13
|
+
lambda do
|
14
|
+
klass.new({})
|
15
|
+
klass.new :foo => "bar"
|
16
|
+
end.should_not raise_error
|
222
17
|
end
|
223
|
-
|
224
|
-
propfind('http://localhost/', :input => xml)
|
225
|
-
|
226
|
-
multistatus_response('/href').first.text.strip.should == 'http://localhost/'
|
227
18
|
|
228
|
-
|
229
|
-
|
230
|
-
|
19
|
+
it "sets options from argument" do
|
20
|
+
instance = klass.new :foo => "bar"
|
21
|
+
instance.options[:foo].should == "bar"
|
231
22
|
end
|
232
|
-
end
|
233
|
-
|
234
|
-
it 'should find named properties' do
|
235
|
-
put('/test.html', :input => '<html/>').should be_ok
|
236
|
-
propfind('/test.html', :input => propfind_xml(:getcontenttype, :getcontentlength))
|
237
|
-
|
238
|
-
multistatus_response('/propstat/prop/getcontenttype').first.text.should == 'text/html'
|
239
|
-
multistatus_response('/propstat/prop/getcontentlength').first.text.should == '7'
|
240
|
-
end
|
241
23
|
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
xml = render do |xml|
|
246
|
-
xml.lockinfo('xmlns:d' => "DAV:") do
|
247
|
-
xml.lockscope { xml.exclusive }
|
248
|
-
xml.locktype { xml.write }
|
249
|
-
xml.owner { xml.href "http://test.de/" }
|
250
|
-
end
|
24
|
+
it "defaults option :resource_class to FileResource" do
|
25
|
+
instance = klass.new
|
26
|
+
instance.options[:resource_class].should be(RackDAV::FileResource)
|
251
27
|
end
|
252
28
|
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
REXML::XPath::match(response_xml, "/prop/lockdiscovery/activelock" + pattern, '' => 'DAV:')
|
29
|
+
it "defaults option :root to current directory" do
|
30
|
+
path = File.expand_path("../../bin", __FILE__)
|
31
|
+
Dir.chdir(path)
|
32
|
+
instance = klass.new
|
33
|
+
instance.options[:root].should == path
|
259
34
|
end
|
260
|
-
|
261
|
-
match[''].should_not be_empty
|
262
|
-
|
263
|
-
match['/locktype'].should_not be_empty
|
264
|
-
match['/lockscope'].should_not be_empty
|
265
|
-
match['/depth'].should_not be_empty
|
266
|
-
match['/owner'].should_not be_empty
|
267
|
-
match['/timeout'].should_not be_empty
|
268
|
-
match['/locktoken'].should_not be_empty
|
269
35
|
end
|
36
|
+
|
270
37
|
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
require 'rspec'
|
2
|
+
require 'rack_dav'
|
3
|
+
|
4
|
+
unless defined?(SPEC_ROOT)
|
5
|
+
SPEC_ROOT = File.expand_path("../", __FILE__)
|
6
|
+
end
|
7
|
+
|
8
|
+
module Helpers
|
9
|
+
|
10
|
+
private
|
11
|
+
|
12
|
+
# Gets the currently described class.
|
13
|
+
# Conversely to +subject+, it returns the class
|
14
|
+
# instead of an instance.
|
15
|
+
def klass
|
16
|
+
described_class
|
17
|
+
end
|
18
|
+
|
19
|
+
def fixture(*names)
|
20
|
+
File.join(SPEC_ROOT, "fixtures", *names)
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
24
|
+
|
25
|
+
RSpec.configure do |config|
|
26
|
+
config.include Helpers
|
27
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
module RackDAV
|
2
|
+
|
3
|
+
# Quick & Dirty
|
4
|
+
class LockableFileResource < FileResource
|
5
|
+
@@locks = {}
|
6
|
+
|
7
|
+
def lock(token, timeout, scope = nil, type = nil, owner = nil)
|
8
|
+
if scope && type && owner
|
9
|
+
# Create lock
|
10
|
+
@@locks[token] = {
|
11
|
+
:timeout => timeout,
|
12
|
+
:scope => scope,
|
13
|
+
:type => type,
|
14
|
+
:owner => owner
|
15
|
+
}
|
16
|
+
return true
|
17
|
+
else
|
18
|
+
# Refresh lock
|
19
|
+
lock = @@locks[token]
|
20
|
+
return false unless lock
|
21
|
+
return [ lock[:timeout], lock[:scope], lock[:type], lock[:owner] ]
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def unlock(token)
|
26
|
+
!!@@locks.delete(token)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
metadata
CHANGED
@@ -1,79 +1,136 @@
|
|
1
|
-
--- !ruby/object:Gem::Specification
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
2
|
name: rack_dav
|
3
|
-
version: !ruby/object:Gem::Version
|
4
|
-
|
5
|
-
prerelease:
|
6
|
-
segments:
|
7
|
-
- 0
|
8
|
-
- 1
|
9
|
-
- 3
|
10
|
-
version: 0.1.3
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.3.1
|
5
|
+
prerelease:
|
11
6
|
platform: ruby
|
12
|
-
authors:
|
7
|
+
authors:
|
13
8
|
- Matthias Georgi
|
14
9
|
autorequire:
|
15
10
|
bindir: bin
|
16
11
|
cert_chain: []
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
12
|
+
date: 2013-01-24 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: rack
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ~>
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: 1.4.0
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ~>
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: 1.4.0
|
30
|
+
- !ruby/object:Gem::Dependency
|
31
|
+
name: nokogiri
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
33
|
+
none: false
|
34
|
+
requirements:
|
35
|
+
- - ! '>='
|
36
|
+
- !ruby/object:Gem::Version
|
37
|
+
version: '0'
|
38
|
+
type: :runtime
|
39
|
+
prerelease: false
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ! '>='
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: '0'
|
46
|
+
- !ruby/object:Gem::Dependency
|
47
|
+
name: rspec
|
48
|
+
requirement: !ruby/object:Gem::Requirement
|
49
|
+
none: false
|
50
|
+
requirements:
|
51
|
+
- - ~>
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: 2.11.0
|
54
|
+
type: :development
|
55
|
+
prerelease: false
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
57
|
+
none: false
|
58
|
+
requirements:
|
59
|
+
- - ~>
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: 2.11.0
|
62
|
+
- !ruby/object:Gem::Dependency
|
63
|
+
name: rake
|
64
|
+
requirement: !ruby/object:Gem::Requirement
|
65
|
+
none: false
|
66
|
+
requirements:
|
67
|
+
- - ~>
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
version: 0.9.0
|
70
|
+
type: :development
|
71
|
+
prerelease: false
|
72
|
+
version_requirements: !ruby/object:Gem::Requirement
|
73
|
+
none: false
|
74
|
+
requirements:
|
75
|
+
- - ~>
|
76
|
+
- !ruby/object:Gem::Version
|
77
|
+
version: 0.9.0
|
78
|
+
description: WebDAV handler for Rack.
|
23
79
|
email: matti.georgi@gmail.com
|
24
|
-
executables:
|
80
|
+
executables:
|
25
81
|
- rack_dav
|
26
82
|
extensions: []
|
27
|
-
|
28
|
-
extra_rdoc_files:
|
83
|
+
extra_rdoc_files:
|
29
84
|
- README.md
|
30
|
-
files:
|
85
|
+
files:
|
31
86
|
- .gitignore
|
87
|
+
- CHANGELOG.md
|
88
|
+
- Gemfile
|
89
|
+
- Gemfile.lock
|
32
90
|
- LICENSE
|
33
|
-
-
|
91
|
+
- README.md
|
92
|
+
- Rakefile
|
93
|
+
- bin/rack_dav
|
34
94
|
- lib/rack_dav.rb
|
95
|
+
- lib/rack_dav/controller.rb
|
35
96
|
- lib/rack_dav/file_resource.rb
|
36
97
|
- lib/rack_dav/handler.rb
|
37
|
-
- lib/rack_dav/controller.rb
|
38
|
-
- lib/rack_dav/builder_namespace.rb
|
39
98
|
- lib/rack_dav/http_status.rb
|
40
99
|
- lib/rack_dav/resource.rb
|
41
|
-
-
|
100
|
+
- lib/rack_dav/string.rb
|
101
|
+
- lib/rack_dav/version.rb
|
102
|
+
- rack_dav.gemspec
|
103
|
+
- spec/controller_spec.rb
|
104
|
+
- spec/file_resource_spec.rb
|
105
|
+
- spec/fixtures/folder/01.txt
|
106
|
+
- spec/fixtures/folder/02.txt
|
107
|
+
- spec/fixtures/requests/lock.xml
|
42
108
|
- spec/handler_spec.rb
|
43
|
-
-
|
44
|
-
|
45
|
-
homepage: http://
|
109
|
+
- spec/spec_helper.rb
|
110
|
+
- spec/support/lockable_file_resource.rb
|
111
|
+
homepage: http://georgi.github.com/rack_dav
|
46
112
|
licenses: []
|
47
|
-
|
48
113
|
post_install_message:
|
49
114
|
rdoc_options: []
|
50
|
-
|
51
|
-
require_paths:
|
115
|
+
require_paths:
|
52
116
|
- lib
|
53
|
-
required_ruby_version: !ruby/object:Gem::Requirement
|
117
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
54
118
|
none: false
|
55
|
-
requirements:
|
56
|
-
- -
|
57
|
-
- !ruby/object:Gem::Version
|
58
|
-
|
59
|
-
|
60
|
-
- 0
|
61
|
-
version: "0"
|
62
|
-
required_rubygems_version: !ruby/object:Gem::Requirement
|
119
|
+
requirements:
|
120
|
+
- - ! '>='
|
121
|
+
- !ruby/object:Gem::Version
|
122
|
+
version: '0'
|
123
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
63
124
|
none: false
|
64
|
-
requirements:
|
65
|
-
- -
|
66
|
-
- !ruby/object:Gem::Version
|
67
|
-
|
68
|
-
segments:
|
69
|
-
- 0
|
70
|
-
version: "0"
|
125
|
+
requirements:
|
126
|
+
- - ! '>='
|
127
|
+
- !ruby/object:Gem::Version
|
128
|
+
version: '0'
|
71
129
|
requirements: []
|
72
|
-
|
73
130
|
rubyforge_project:
|
74
|
-
rubygems_version: 1.
|
131
|
+
rubygems_version: 1.8.24
|
75
132
|
signing_key:
|
76
133
|
specification_version: 3
|
77
|
-
summary: WebDAV handler for Rack
|
134
|
+
summary: WebDAV handler for Rack.
|
78
135
|
test_files: []
|
79
|
-
|
136
|
+
has_rdoc:
|