rack_dav 0.1.3 → 0.3.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,11 @@
1
+ require 'spec_helper'
2
+
3
+ describe RackDAV::FileResource do
4
+
5
+ describe "#get" do
6
+ context "with a directory" do
7
+ # it "sets a compliant rack response"
8
+ end
9
+ end
10
+
11
+ end
File without changes
File without changes
@@ -0,0 +1,12 @@
1
+ <?xml version="1.0" encoding="utf-8" ?>
2
+ <D:lockinfo xmlns:D='DAV:'>
3
+ <D:lockscope>
4
+ <D:exclusive/>
5
+ </D:lockscope>
6
+ <D:locktype>
7
+ <D:write/>
8
+ </D:locktype>
9
+ <D:owner>
10
+ <D:href>http://example.com/</D:href>
11
+ </D:owner>
12
+ </D:lockinfo>
data/spec/handler_spec.rb CHANGED
@@ -1,270 +1,37 @@
1
- $:.unshift(File.expand_path(File.dirname(__FILE__) + '/../lib'))
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
- def propfind_xml(*props)
63
- render do |xml|
64
- xml.propfind('xmlns:d' => "DAV:") do
65
- xml.prop do
66
- props.each do |prop|
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
- it 'should not allow copy to another domain' do
127
- put('/test', :input => 'body').should be_ok
128
- copy('http://localhost/', 'HTTP_DESTINATION' => 'http://another/').should be_bad_gateway
129
- end
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
- props = %w(creationdate displayname getlastmodified getetag resourcetype getcontenttype getcontentlength)
229
- props.each do |prop|
230
- multistatus_response('/propstat/prop/' + prop).should_not be_empty
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
- it 'should lock a resource' do
243
- put('/test', :input => 'body').should be_ok
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
- lock('/test', :input => xml)
254
-
255
- response.should be_ok
256
-
257
- match = lambda do |pattern|
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
@@ -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
- hash: 29
5
- prerelease: false
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
- date: 2010-10-23 00:00:00 -07:00
19
- default_executable:
20
- dependencies: []
21
-
22
- description: WebDAV handler for Rack
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
- - rack_dav.gemspec
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
- - bin/rack_dav
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
- - README.md
44
- has_rdoc: true
45
- homepage: http://www.matthias-georgi.de/rack_dav
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
- hash: 3
59
- segments:
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
- hash: 3
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.3.7
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: