webdav 0.0.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.
- checksums.yaml +7 -0
- data/Gemfile +3 -0
- data/LICENSE +21 -0
- data/README.md +148 -0
- data/lib/Net/HTTP/Report.rb +16 -0
- data/lib/String/to_const.rb +7 -0
- data/lib/Thoran/Array/AllButFirst/all_but_first.rb +28 -0
- data/lib/Thoran/Array/FirstX/firstX.rb +32 -0
- data/lib/Thoran/String/ToConst/to_const.rb +47 -0
- data/lib/WebDAV/Error.rb +17 -0
- data/lib/WebDAV/MultiStatus.rb +32 -0
- data/lib/WebDAV/Response.rb +29 -0
- data/lib/WebDAV/VERSION.rb +6 -0
- data/lib/webdav.rb +158 -0
- data/test/WebDAV/Error_test.rb +85 -0
- data/test/WebDAV/MultiStatus_test.rb +102 -0
- data/test/WebDAV/Response_test.rb +60 -0
- data/test/helper.rb +13 -0
- data/test/webdav_test.rb +254 -0
- data/webdav.gemspec +29 -0
- metadata +72 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: 1721f681cfb816034b039f48242101233a096ac8ca65eee54ff8e7cac6e419eb
|
|
4
|
+
data.tar.gz: 3c8cd82ee3191f3cb957b63c4be8c2d86f362fb470bbd9f5989f8548a417580a
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: f8af2a0aaa9a4388ede4af98c93a0303b27c803a6233ec357e0a118098bd95afdd032f005e8c075d0133ad6fc9159871bf24007b1165b0dbadf5b1b281cb06a3
|
|
7
|
+
data.tar.gz: 7b680af432e9913a56c163c199d9908cdbc657bf4246175e8a3cd76030116adb66764c8cfc8b270c5a6e575875032d597a63be23fad374a3f6b48e57c85cd800
|
data/Gemfile
ADDED
data/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 thoran
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
data/README.md
ADDED
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
# webdav
|
|
2
|
+
|
|
3
|
+
A Ruby WebDAV client library.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```
|
|
8
|
+
gem install webdav
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
Or in your Gemfile:
|
|
12
|
+
|
|
13
|
+
```ruby
|
|
14
|
+
gem 'webdav'
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
## Usage
|
|
18
|
+
|
|
19
|
+
```ruby
|
|
20
|
+
require 'webdav'
|
|
21
|
+
|
|
22
|
+
dav = WebDAV.new('https://dav.example.com/files/', username: 'user', password: 'pass')
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
### Discovering resources
|
|
26
|
+
|
|
27
|
+
```ruby
|
|
28
|
+
response = dav.propfind('/', depth: '1')
|
|
29
|
+
response.resources.each do |resource|
|
|
30
|
+
puts resource[:href]
|
|
31
|
+
puts resource[:properties]
|
|
32
|
+
end
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
### Reading a resource
|
|
36
|
+
|
|
37
|
+
```ruby
|
|
38
|
+
response = dav.get('/documents/report.txt')
|
|
39
|
+
puts response.body
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
### Writing a resource
|
|
43
|
+
|
|
44
|
+
```ruby
|
|
45
|
+
dav.put('/documents/report.txt', body: 'Hello, world.', content_type: 'text/plain')
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
### Deleting a resource
|
|
49
|
+
|
|
50
|
+
```ruby
|
|
51
|
+
dav.delete('/documents/report.txt')
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
### Creating a collection
|
|
55
|
+
|
|
56
|
+
```ruby
|
|
57
|
+
dav.mkcol('/documents/archive/')
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
### Copying and moving
|
|
61
|
+
|
|
62
|
+
```ruby
|
|
63
|
+
dav.copy('/documents/report.txt', to: '/archive/report.txt')
|
|
64
|
+
dav.move('/documents/draft.txt', to: '/documents/final.txt')
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
### Locking and unlocking
|
|
68
|
+
|
|
69
|
+
```ruby
|
|
70
|
+
lock_body = <<~XML
|
|
71
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
72
|
+
<d:lockinfo xmlns:d="DAV:">
|
|
73
|
+
<d:lockscope><d:exclusive/></d:lockscope>
|
|
74
|
+
<d:locktype><d:write/></d:locktype>
|
|
75
|
+
</d:lockinfo>
|
|
76
|
+
XML
|
|
77
|
+
|
|
78
|
+
response = dav.lock('/documents/report.txt', body: lock_body)
|
|
79
|
+
# ...
|
|
80
|
+
dav.unlock('/documents/report.txt', token: 'urn:uuid:...')
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
### REPORT
|
|
84
|
+
|
|
85
|
+
```ruby
|
|
86
|
+
response = dav.report('/calendars/user/', body: report_xml, depth: '1')
|
|
87
|
+
response.resources.each do |resource|
|
|
88
|
+
puts resource[:href]
|
|
89
|
+
end
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
## Verbs
|
|
93
|
+
|
|
94
|
+
### WebDAV (RFC 4918 / RFC 3253)
|
|
95
|
+
|
|
96
|
+
- `propfind(path, body:, depth:)` — Retrieve properties
|
|
97
|
+
- `proppatch(path, body:)` — Modify properties
|
|
98
|
+
- `report(path, body:, depth:)` — Run a report query
|
|
99
|
+
- `mkcol(path)` — Create a collection
|
|
100
|
+
- `copy(path, to:, depth:, overwrite:)` — Copy a resource
|
|
101
|
+
- `move(path, to:, overwrite:)` — Move a resource
|
|
102
|
+
- `lock(path, body:)` — Lock a resource
|
|
103
|
+
- `unlock(path, token:)` — Unlock a resource
|
|
104
|
+
|
|
105
|
+
### Standard HTTP
|
|
106
|
+
|
|
107
|
+
- `get(path)`
|
|
108
|
+
- `head(path)`
|
|
109
|
+
- `post(path, body:, content_type:)`
|
|
110
|
+
- `put(path, body:, content_type:)`
|
|
111
|
+
- `patch(path, body:, content_type:)`
|
|
112
|
+
- `delete(path)`
|
|
113
|
+
- `options(path)`
|
|
114
|
+
- `trace(path)`
|
|
115
|
+
|
|
116
|
+
## Responses
|
|
117
|
+
|
|
118
|
+
All methods return either a `WebDAV::Response` or a `WebDAV::MultiStatus`.
|
|
119
|
+
|
|
120
|
+
`WebDAV::Response` provides
|
|
121
|
+
|
|
122
|
+
- `code`
|
|
123
|
+
- `message`
|
|
124
|
+
- `headers`
|
|
125
|
+
- `body`
|
|
126
|
+
- `etag`
|
|
127
|
+
- `content_type`
|
|
128
|
+
- `success?`
|
|
129
|
+
|
|
130
|
+
`WebDAV::MultiStatus` additionally provides:
|
|
131
|
+
|
|
132
|
+
- `resources`
|
|
133
|
+
— an array of hashes, each with:
|
|
134
|
+
- `href`
|
|
135
|
+
- `properties`
|
|
136
|
+
- `status`
|
|
137
|
+
|
|
138
|
+
## Errors
|
|
139
|
+
|
|
140
|
+
Responses with status >= 400 raise `WebDAV::Error`, which has `code`, `message`, and `body`.
|
|
141
|
+
|
|
142
|
+
## Dependencies
|
|
143
|
+
|
|
144
|
+
- [http.rb](https://github.com/thoran/http.rb)
|
|
145
|
+
|
|
146
|
+
## Licence
|
|
147
|
+
|
|
148
|
+
MIT
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# Thoran/Array/AllButFirst/all_but_first.rb
|
|
2
|
+
# Thoran::Array::AllButFirst#all_but_first
|
|
3
|
+
|
|
4
|
+
# 20141223
|
|
5
|
+
# 0.1.0
|
|
6
|
+
|
|
7
|
+
# Description: This returns a copy of the receiving array with the first element removed.
|
|
8
|
+
|
|
9
|
+
# Changes:
|
|
10
|
+
# 1. + Thoran namespace.
|
|
11
|
+
|
|
12
|
+
require 'Thoran/Array/FirstX/firstX'
|
|
13
|
+
|
|
14
|
+
module Thoran
|
|
15
|
+
module Array
|
|
16
|
+
module AllButFirst
|
|
17
|
+
|
|
18
|
+
def all_but_first
|
|
19
|
+
d = self.dup
|
|
20
|
+
d.first!
|
|
21
|
+
d
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
Array.send(:include, Thoran::Array::AllButFirst)
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# Thoran/Array/FirstX/firstX.rb
|
|
2
|
+
# Thoran::Array::FirstX#first!
|
|
3
|
+
|
|
4
|
+
# 20180804
|
|
5
|
+
# 0.3.3
|
|
6
|
+
|
|
7
|
+
# Description: Sometimes it makes more sense to treat arrays this way.
|
|
8
|
+
|
|
9
|
+
# Changes since 0.2:
|
|
10
|
+
# 1. Added the original version 0.1.0 of the implementation to the later 0.1.0!
|
|
11
|
+
# 0/1
|
|
12
|
+
# 2. Switched the tests to spec-style.
|
|
13
|
+
# 1/2
|
|
14
|
+
# 3. Added a test for the state of the array afterward, since this is meant to be an in place change.
|
|
15
|
+
# 2/3
|
|
16
|
+
# 4. Added tests for the extended functionality introduced in the first version 0.1.0.
|
|
17
|
+
|
|
18
|
+
module Thoran
|
|
19
|
+
module Array
|
|
20
|
+
module FirstX
|
|
21
|
+
|
|
22
|
+
def first!(n = 1)
|
|
23
|
+
return_value = []
|
|
24
|
+
n.times{return_value << self.shift}
|
|
25
|
+
return_value.size == 1 ? return_value[0] : return_value
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
Array.send(:include, Thoran::Array::FirstX)
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# Thoran/String/ToConst/to_const.rb
|
|
2
|
+
# Thoran::String::ToConst#to_const
|
|
3
|
+
|
|
4
|
+
# 20141223
|
|
5
|
+
# 0.2.0
|
|
6
|
+
|
|
7
|
+
# Description: This takes a string and returns a constant, with unlimited namespacing.
|
|
8
|
+
|
|
9
|
+
# History: Derived from Object#to_const 0.3.0, and superceding Object#to_const.
|
|
10
|
+
|
|
11
|
+
# Changes:
|
|
12
|
+
# 1. + Thoran namespace.
|
|
13
|
+
|
|
14
|
+
# Todo:
|
|
15
|
+
# 1. This only works for two levels of constants. Three and you're stuffed. So, this needs to be recursive...
|
|
16
|
+
# Done iteratively as of 0.1.0.
|
|
17
|
+
# 2. Make this work for symbols. However, this will only work if there's no namespacing. ie. :A is OK, but :A::B is not.
|
|
18
|
+
|
|
19
|
+
# Discussion:
|
|
20
|
+
# 1. Should this go separately into classes for which ::const_get will work and be removed from Object? Done as of 0.1.0.
|
|
21
|
+
|
|
22
|
+
require 'Thoran/Array/AllButFirst/all_but_first'
|
|
23
|
+
|
|
24
|
+
module Thoran
|
|
25
|
+
module String
|
|
26
|
+
module ToConst
|
|
27
|
+
|
|
28
|
+
def to_const
|
|
29
|
+
if self =~ /::/
|
|
30
|
+
constants = self.split('::')
|
|
31
|
+
constant = Object.const_get(constants.first)
|
|
32
|
+
constants = constants.all_but_first
|
|
33
|
+
until constants.empty? do
|
|
34
|
+
constant = constant.const_get(constants.shift)
|
|
35
|
+
end
|
|
36
|
+
else
|
|
37
|
+
constant = Object.const_get(self)
|
|
38
|
+
end
|
|
39
|
+
constant
|
|
40
|
+
end
|
|
41
|
+
alias_method :to_constant, :to_const
|
|
42
|
+
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
String.send(:include, Thoran::String::ToConst)
|
data/lib/WebDAV/Error.rb
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# WebDAV/Error.rb
|
|
2
|
+
# WebDAV::Error.rb
|
|
3
|
+
|
|
4
|
+
class WebDAV
|
|
5
|
+
class Error < StandardError
|
|
6
|
+
attr_reader :code, :message, :body
|
|
7
|
+
|
|
8
|
+
private
|
|
9
|
+
|
|
10
|
+
def initialize(response)
|
|
11
|
+
@code = response.code.to_i
|
|
12
|
+
@message = response.message
|
|
13
|
+
@body = response.body
|
|
14
|
+
super("HTTP #{@code}: #{@message}")
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# WebDAV/MultiStatus.rb
|
|
2
|
+
# WebDAV::MultiStatus
|
|
3
|
+
|
|
4
|
+
require_relative './Response'
|
|
5
|
+
|
|
6
|
+
class WebDAV
|
|
7
|
+
class MultiStatus < Response
|
|
8
|
+
attr_reader :resources
|
|
9
|
+
|
|
10
|
+
def parse
|
|
11
|
+
doc = REXML::Document.new(body)
|
|
12
|
+
resources = []
|
|
13
|
+
doc.elements.each('//d:response') do |resp|
|
|
14
|
+
href = resp.elements['.//d:href']&.text
|
|
15
|
+
properties = {}
|
|
16
|
+
resp.elements.each('.//d:prop/*') do |prop|
|
|
17
|
+
properties[prop.name] = prop.text || prop.to_s
|
|
18
|
+
end
|
|
19
|
+
status = resp.elements['.//d:status']&.text
|
|
20
|
+
resources << {href: href, properties: properties, status: status}
|
|
21
|
+
end
|
|
22
|
+
resources
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
private
|
|
26
|
+
|
|
27
|
+
def initialize(response)
|
|
28
|
+
super
|
|
29
|
+
@resources = parse
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# WebDAV/Response.rb
|
|
2
|
+
# WebDAV::Response.rb
|
|
3
|
+
|
|
4
|
+
class WebDAV
|
|
5
|
+
class Response
|
|
6
|
+
attr_reader :code, :message, :headers, :body
|
|
7
|
+
|
|
8
|
+
def success?
|
|
9
|
+
code < 400
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def etag
|
|
13
|
+
headers['ETag']
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def content_type
|
|
17
|
+
headers['Content-Type']
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
private
|
|
21
|
+
|
|
22
|
+
def initialize(response)
|
|
23
|
+
@code = response.code.to_i
|
|
24
|
+
@message = response.message
|
|
25
|
+
@headers = response
|
|
26
|
+
@body = response.body
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
data/lib/webdav.rb
ADDED
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
# WebDAV.rb
|
|
2
|
+
# WebDAV
|
|
3
|
+
|
|
4
|
+
gem 'http.rb'; require 'http.rb'
|
|
5
|
+
require 'net/http'
|
|
6
|
+
require 'rexml/document'
|
|
7
|
+
require 'uri'
|
|
8
|
+
|
|
9
|
+
require_relative './Net/HTTP/Report'
|
|
10
|
+
require_relative './String/to_const'
|
|
11
|
+
require_relative './WebDAV/Error'
|
|
12
|
+
require_relative './WebDAV/MultiStatus'
|
|
13
|
+
require_relative './WebDAV/Response'
|
|
14
|
+
|
|
15
|
+
class WebDAV
|
|
16
|
+
|
|
17
|
+
# Properties
|
|
18
|
+
|
|
19
|
+
def propfind(path = '/', body: nil, depth: '1')
|
|
20
|
+
response = request(:propfind, path, body: body, headers: {'Depth' => depth})
|
|
21
|
+
handle_response(response)
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def proppatch(path, body:)
|
|
25
|
+
response = request(:proppatch, path, body: body)
|
|
26
|
+
handle_response(response)
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
# Reports
|
|
30
|
+
|
|
31
|
+
def report(path, body:, depth: '1')
|
|
32
|
+
response = request(:report, path, body: body, headers: {'Depth' => depth})
|
|
33
|
+
handle_response(response)
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
# Collections
|
|
37
|
+
|
|
38
|
+
def mkcol(path)
|
|
39
|
+
response = request(:mkcol, path)
|
|
40
|
+
handle_response(response)
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
# Namespace
|
|
44
|
+
|
|
45
|
+
def copy(path, to:, depth: 'infinity', overwrite: true)
|
|
46
|
+
response = request(:copy, path, headers: {
|
|
47
|
+
'Destination' => resolve_uri(to).to_s,
|
|
48
|
+
'Depth' => depth,
|
|
49
|
+
'Overwrite' => overwrite ? 'T' : 'F'
|
|
50
|
+
})
|
|
51
|
+
handle_response(response)
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def move(path, to:, overwrite: true)
|
|
55
|
+
response = request(:move, path, headers: {
|
|
56
|
+
'Destination' => resolve_uri(to).to_s,
|
|
57
|
+
'Overwrite' => overwrite ? 'T' : 'F'
|
|
58
|
+
})
|
|
59
|
+
handle_response(response)
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
# Locking
|
|
63
|
+
|
|
64
|
+
def lock(path, body:)
|
|
65
|
+
response = request(:lock, path, body: body)
|
|
66
|
+
handle_response(response)
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def unlock(path, token:)
|
|
70
|
+
response = request(:unlock, path, headers: {'Lock-Token' => "<#{token}>"})
|
|
71
|
+
handle_response(response)
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
# Standard HTTP
|
|
75
|
+
|
|
76
|
+
def get(path)
|
|
77
|
+
response = request(:get, path)
|
|
78
|
+
handle_response(response)
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
def head(path)
|
|
82
|
+
response = request(:head, path)
|
|
83
|
+
handle_response(response)
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def post(path, body:, content_type: 'application/xml')
|
|
87
|
+
response = request(:post, path, body: body, headers: {'Content-Type' => content_type})
|
|
88
|
+
handle_response(response)
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
def put(path, body:, content_type: 'application/octet-stream')
|
|
92
|
+
response = request(:put, path, body: body, headers: {'Content-Type' => content_type})
|
|
93
|
+
handle_response(response)
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
def patch(path, body:, content_type: 'application/xml')
|
|
97
|
+
response = request(:patch, path, body: body, headers: {'Content-Type' => content_type})
|
|
98
|
+
handle_response(response)
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
def delete(path)
|
|
102
|
+
response = request(:delete, path)
|
|
103
|
+
handle_response(response)
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
def options(path = '/')
|
|
107
|
+
response = request(:options, path)
|
|
108
|
+
handle_response(response)
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
def trace(path)
|
|
112
|
+
response = request(:trace, path)
|
|
113
|
+
handle_response(response)
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
private
|
|
117
|
+
|
|
118
|
+
def initialize(uri, username:, password:)
|
|
119
|
+
@uri = uri.is_a?(URI) ? uri : URI.parse(uri)
|
|
120
|
+
@username = username
|
|
121
|
+
@password = password
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
def resolve_uri(path)
|
|
125
|
+
URI.join(@uri, path)
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
def request_uri(path)
|
|
129
|
+
resolve_uri(path).request_uri
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
def default_headers
|
|
133
|
+
{'Content-Type' => 'application/xml; charset=utf-8'}
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
def request_class(verb)
|
|
137
|
+
"Net::HTTP::#{verb.to_s.capitalize}".to_const
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
def request(verb, path, body: nil, headers: {})
|
|
141
|
+
request_object = request_class(verb).new(request_uri(path))
|
|
142
|
+
request_object.basic_auth(@username, @password)
|
|
143
|
+
request_object.body = body if body
|
|
144
|
+
merged_headers = default_headers.merge(headers)
|
|
145
|
+
merged_headers.each{|k, v| request_object[k] = v}
|
|
146
|
+
HTTP.request(resolve_uri(path), request_object)
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
def handle_response(response)
|
|
150
|
+
raise WebDAV::Error.new(response) if response.code.to_i >= 400
|
|
151
|
+
case response.code.to_i
|
|
152
|
+
when 207
|
|
153
|
+
MultiStatus.new(response)
|
|
154
|
+
else
|
|
155
|
+
Response.new(response)
|
|
156
|
+
end
|
|
157
|
+
end
|
|
158
|
+
end
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
# test/WebDAV/Error_test.rb
|
|
2
|
+
|
|
3
|
+
require_relative '../helper'
|
|
4
|
+
|
|
5
|
+
describe WebDAV::Error do
|
|
6
|
+
let(:mock_response) do
|
|
7
|
+
MockResponse.new(
|
|
8
|
+
code: '404',
|
|
9
|
+
message: 'Not Found',
|
|
10
|
+
body: '<!DOCTYPE html><html><body>Not Found</body></html>'
|
|
11
|
+
)
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
let(:error){WebDAV::Error.new(mock_response)}
|
|
15
|
+
|
|
16
|
+
describe "#code" do
|
|
17
|
+
it "returns the status code as an integer" do
|
|
18
|
+
_(error.code).must_equal 404
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
describe "#message" do
|
|
23
|
+
it "returns the status message" do
|
|
24
|
+
_(error.message).must_equal 'Not Found'
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
describe "#body" do
|
|
29
|
+
it "returns the response body" do
|
|
30
|
+
_(error.body).must_include 'Not Found'
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
describe "#to_s" do
|
|
35
|
+
it "includes the status code and message" do
|
|
36
|
+
_(error.to_s).must_equal 'HTTP 404: Not Found'
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
describe "inheritance" do
|
|
41
|
+
it "is a StandardError" do
|
|
42
|
+
_(error).must_be_kind_of StandardError
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
it "can be raised and rescued" do
|
|
46
|
+
assert_raises(WebDAV::Error) do
|
|
47
|
+
raise WebDAV::Error.new(mock_response)
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
describe "with a 401 status code" do
|
|
53
|
+
let(:mock_response) do
|
|
54
|
+
MockResponse.new(
|
|
55
|
+
code: '401',
|
|
56
|
+
message: 'Unauthorized',
|
|
57
|
+
body: ''
|
|
58
|
+
)
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
let(:error){WebDAV::Error.new(mock_response)}
|
|
62
|
+
|
|
63
|
+
it "handles a 401 error" do
|
|
64
|
+
_(error.code).must_equal 401
|
|
65
|
+
_(error.to_s).must_equal 'HTTP 401: Unauthorized'
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
describe "with a 500 status code" do
|
|
70
|
+
let(:mock_response) do
|
|
71
|
+
MockResponse.new(
|
|
72
|
+
code: '500',
|
|
73
|
+
message: 'Internal Server Error',
|
|
74
|
+
body: ''
|
|
75
|
+
)
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
let(:error){WebDAV::Error.new(mock_response)}
|
|
79
|
+
|
|
80
|
+
it "handles a 500 error" do
|
|
81
|
+
_(error.code).must_equal 500
|
|
82
|
+
_(error.to_s).must_equal 'HTTP 500: Internal Server Error'
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
end
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
# test/WebDAV/MultiStatus_test.rb
|
|
2
|
+
|
|
3
|
+
require_relative '../helper'
|
|
4
|
+
|
|
5
|
+
describe WebDAV::MultiStatus do
|
|
6
|
+
let(:multistatus_xml) do
|
|
7
|
+
<<~XML
|
|
8
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
9
|
+
<d:multistatus xmlns:d="DAV:">
|
|
10
|
+
<d:response>
|
|
11
|
+
<d:href>/dav/calendars/user/test/calendar1/</d:href>
|
|
12
|
+
<d:propstat>
|
|
13
|
+
<d:prop>
|
|
14
|
+
<d:displayname>Work</d:displayname>
|
|
15
|
+
<d:resourcetype><d:collection/></d:resourcetype>
|
|
16
|
+
</d:prop>
|
|
17
|
+
<d:status>HTTP/1.1 200 OK</d:status>
|
|
18
|
+
</d:propstat>
|
|
19
|
+
</d:response>
|
|
20
|
+
<d:response>
|
|
21
|
+
<d:href>/dav/calendars/user/test/calendar2/</d:href>
|
|
22
|
+
<d:propstat>
|
|
23
|
+
<d:prop>
|
|
24
|
+
<d:displayname>Personal</d:displayname>
|
|
25
|
+
<d:resourcetype><d:collection/></d:resourcetype>
|
|
26
|
+
</d:prop>
|
|
27
|
+
<d:status>HTTP/1.1 200 OK</d:status>
|
|
28
|
+
</d:propstat>
|
|
29
|
+
</d:response>
|
|
30
|
+
</d:multistatus>
|
|
31
|
+
XML
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
let(:mock_response) do
|
|
35
|
+
MockResponse.new(
|
|
36
|
+
code: '207',
|
|
37
|
+
message: 'Multi-Status',
|
|
38
|
+
body: multistatus_xml
|
|
39
|
+
)
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
let(:response){WebDAV::MultiStatus.new(mock_response)}
|
|
43
|
+
|
|
44
|
+
describe "#code" do
|
|
45
|
+
it "returns 207" do
|
|
46
|
+
_(response.code).must_equal 207
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
describe "#success?" do
|
|
51
|
+
it "returns true" do
|
|
52
|
+
_(response.success?).must_equal true
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
describe "#resources" do
|
|
57
|
+
it "returns an array" do
|
|
58
|
+
_(response.resources).must_be_kind_of Array
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
it "parses the correct number of resources" do
|
|
62
|
+
_(response.resources.length).must_equal 2
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
it "extracts href from each resource" do
|
|
66
|
+
_(response.resources[0][:href]).must_equal '/dav/calendars/user/test/calendar1/'
|
|
67
|
+
_(response.resources[1][:href]).must_equal '/dav/calendars/user/test/calendar2/'
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
it "extracts properties from each resource" do
|
|
71
|
+
_(response.resources[0][:properties]['displayname']).must_equal 'Work'
|
|
72
|
+
_(response.resources[1][:properties]['displayname']).must_equal 'Personal'
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
it "extracts status from each resource" do
|
|
76
|
+
_(response.resources[0][:status]).must_equal 'HTTP/1.1 200 OK'
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
describe "with empty multistatus" do
|
|
81
|
+
let(:empty_xml) do
|
|
82
|
+
<<~XML
|
|
83
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
84
|
+
<d:multistatus xmlns:d="DAV:">
|
|
85
|
+
</d:multistatus>
|
|
86
|
+
XML
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
let(:empty_response) do
|
|
90
|
+
MockResponse.new(
|
|
91
|
+
code: '207',
|
|
92
|
+
message: 'Multi-Status',
|
|
93
|
+
body: empty_xml
|
|
94
|
+
)
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
it "returns an empty array" do
|
|
98
|
+
r = WebDAV::MultiStatus.new(empty_response)
|
|
99
|
+
_(r.resources).must_equal []
|
|
100
|
+
end
|
|
101
|
+
end
|
|
102
|
+
end
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
# test/WebDAV/Response_test.rb
|
|
2
|
+
|
|
3
|
+
require_relative '../helper'
|
|
4
|
+
|
|
5
|
+
describe WebDAV::Response do
|
|
6
|
+
let(:mock_response) do
|
|
7
|
+
headers = {'ETag' => '"abc123"', 'Content-Type' => 'text/html'}
|
|
8
|
+
response = MockResponse.new(
|
|
9
|
+
code: '200',
|
|
10
|
+
message: 'OK',
|
|
11
|
+
body: '<html>Hello</html>',
|
|
12
|
+
headers_hash: headers
|
|
13
|
+
)
|
|
14
|
+
response
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
let(:response){WebDAV::Response.new(mock_response)}
|
|
18
|
+
|
|
19
|
+
describe "#code" do
|
|
20
|
+
it "returns the status code as an integer" do
|
|
21
|
+
_(response.code).must_equal 200
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
describe "#message" do
|
|
26
|
+
it "returns the status message" do
|
|
27
|
+
_(response.message).must_equal 'OK'
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
describe "#body" do
|
|
32
|
+
it "returns the response body" do
|
|
33
|
+
_(response.body).must_equal '<html>Hello</html>'
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
describe "#success?" do
|
|
38
|
+
it "returns true for 2xx responses" do
|
|
39
|
+
_(response.success?).must_equal true
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
it "returns false for 4xx responses" do
|
|
43
|
+
error_response = MockResponse.new(code: '404', message: 'Not Found', body: '')
|
|
44
|
+
r = WebDAV::Response.new(error_response)
|
|
45
|
+
_(r.success?).must_equal false
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
describe "#etag" do
|
|
50
|
+
it "returns the ETag header" do
|
|
51
|
+
_(response.etag).must_equal '"abc123"'
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
describe "#content_type" do
|
|
56
|
+
it "returns the Content-Type header" do
|
|
57
|
+
_(response.content_type).must_equal 'text/html'
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
end
|
data/test/helper.rb
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# test/helper.rb
|
|
2
|
+
|
|
3
|
+
require 'minitest/autorun'
|
|
4
|
+
require 'minitest/mock'
|
|
5
|
+
require 'minitest/spec'
|
|
6
|
+
|
|
7
|
+
require_relative '../lib/webdav'
|
|
8
|
+
|
|
9
|
+
MockResponse = Struct.new(:code, :message, :body, :headers_hash, keyword_init: true) do
|
|
10
|
+
def [](key)
|
|
11
|
+
headers_hash&.dig(key)
|
|
12
|
+
end
|
|
13
|
+
end
|
data/test/webdav_test.rb
ADDED
|
@@ -0,0 +1,254 @@
|
|
|
1
|
+
# test/webdav_test.rb
|
|
2
|
+
|
|
3
|
+
require_relative './helper'
|
|
4
|
+
|
|
5
|
+
describe WebDAV do
|
|
6
|
+
let(:base_uri){'https://dav.example.com/files/'}
|
|
7
|
+
let(:username){'user'}
|
|
8
|
+
let(:password){'pass'}
|
|
9
|
+
|
|
10
|
+
let(:dav) do
|
|
11
|
+
WebDAV.new(base_uri, username: username, password: password)
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
let(:ok_response) do
|
|
15
|
+
MockResponse.new(
|
|
16
|
+
code: '200',
|
|
17
|
+
message: 'OK',
|
|
18
|
+
body: 'Hello'
|
|
19
|
+
)
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
let(:created_response) do
|
|
23
|
+
MockResponse.new(
|
|
24
|
+
code: '201',
|
|
25
|
+
message: 'Created',
|
|
26
|
+
body: ''
|
|
27
|
+
)
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
let(:no_content_response) do
|
|
31
|
+
MockResponse.new(
|
|
32
|
+
code: '204',
|
|
33
|
+
message: 'No Content',
|
|
34
|
+
body: ''
|
|
35
|
+
)
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
let(:multistatus_response) do
|
|
39
|
+
MockResponse.new(
|
|
40
|
+
code: '207',
|
|
41
|
+
message: 'Multi-Status',
|
|
42
|
+
body: <<~XML,
|
|
43
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
44
|
+
<d:multistatus xmlns:d="DAV:">
|
|
45
|
+
<d:response>
|
|
46
|
+
<d:href>/files/doc.txt</d:href>
|
|
47
|
+
<d:propstat>
|
|
48
|
+
<d:prop>
|
|
49
|
+
<d:displayname>doc.txt</d:displayname>
|
|
50
|
+
</d:prop>
|
|
51
|
+
<d:status>HTTP/1.1 200 OK</d:status>
|
|
52
|
+
</d:propstat>
|
|
53
|
+
</d:response>
|
|
54
|
+
</d:multistatus>
|
|
55
|
+
XML
|
|
56
|
+
)
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
let(:not_found_response) do
|
|
60
|
+
MockResponse.new(
|
|
61
|
+
code: '404',
|
|
62
|
+
message: 'Not Found',
|
|
63
|
+
body: 'Not Found'
|
|
64
|
+
)
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
# Properties
|
|
68
|
+
|
|
69
|
+
describe "#propfind" do
|
|
70
|
+
it "returns a MultiStatus response" do
|
|
71
|
+
dav.stub(:request, multistatus_response) do
|
|
72
|
+
result = dav.propfind('/')
|
|
73
|
+
_(result).must_be_kind_of WebDAV::MultiStatus
|
|
74
|
+
_(result.resources.length).must_equal 1
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
describe "#proppatch" do
|
|
80
|
+
it "returns a MultiStatus response" do
|
|
81
|
+
dav.stub(:request, multistatus_response) do
|
|
82
|
+
result = dav.proppatch('/doc.txt', body: '<d:propertyupdate/>')
|
|
83
|
+
_(result).must_be_kind_of WebDAV::MultiStatus
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
# Reports
|
|
89
|
+
|
|
90
|
+
describe "#report" do
|
|
91
|
+
it "returns a MultiStatus response" do
|
|
92
|
+
dav.stub(:request, multistatus_response) do
|
|
93
|
+
result = dav.report('/calendars/', body: '<c:calendar-query/>')
|
|
94
|
+
_(result).must_be_kind_of WebDAV::MultiStatus
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
# Collections
|
|
100
|
+
|
|
101
|
+
describe "#mkcol" do
|
|
102
|
+
it "returns a Response" do
|
|
103
|
+
dav.stub(:request, created_response) do
|
|
104
|
+
result = dav.mkcol('/new_folder/')
|
|
105
|
+
_(result).must_be_kind_of WebDAV::Response
|
|
106
|
+
_(result.code).must_equal 201
|
|
107
|
+
end
|
|
108
|
+
end
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
# Namespace
|
|
112
|
+
|
|
113
|
+
describe "#copy" do
|
|
114
|
+
it "returns a Response" do
|
|
115
|
+
dav.stub(:request, created_response) do
|
|
116
|
+
result = dav.copy('/doc.txt', to: '/archive/doc.txt')
|
|
117
|
+
_(result).must_be_kind_of WebDAV::Response
|
|
118
|
+
end
|
|
119
|
+
end
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
describe "#move" do
|
|
123
|
+
it "returns a Response" do
|
|
124
|
+
dav.stub(:request, created_response) do
|
|
125
|
+
result = dav.move('/draft.txt', to: '/final.txt')
|
|
126
|
+
_(result).must_be_kind_of WebDAV::Response
|
|
127
|
+
end
|
|
128
|
+
end
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
# Locking
|
|
132
|
+
|
|
133
|
+
describe "#lock" do
|
|
134
|
+
it "returns a Response" do
|
|
135
|
+
dav.stub(:request, ok_response) do
|
|
136
|
+
result = dav.lock('/doc.txt', body: '<d:lockinfo/>')
|
|
137
|
+
_(result).must_be_kind_of WebDAV::Response
|
|
138
|
+
end
|
|
139
|
+
end
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
describe "#unlock" do
|
|
143
|
+
it "returns a Response" do
|
|
144
|
+
dav.stub(:request, no_content_response) do
|
|
145
|
+
result = dav.unlock('/doc.txt', token: 'urn:uuid:abc123')
|
|
146
|
+
_(result).must_be_kind_of WebDAV::Response
|
|
147
|
+
_(result.code).must_equal 204
|
|
148
|
+
end
|
|
149
|
+
end
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
# Standard HTTP
|
|
153
|
+
|
|
154
|
+
describe "#get" do
|
|
155
|
+
it "returns a Response with the body" do
|
|
156
|
+
dav.stub(:request, ok_response) do
|
|
157
|
+
result = dav.get('/doc.txt')
|
|
158
|
+
_(result).must_be_kind_of WebDAV::Response
|
|
159
|
+
_(result.body).must_equal 'Hello'
|
|
160
|
+
end
|
|
161
|
+
end
|
|
162
|
+
end
|
|
163
|
+
|
|
164
|
+
describe "#head" do
|
|
165
|
+
it "returns a Response" do
|
|
166
|
+
dav.stub(:request, ok_response) do
|
|
167
|
+
result = dav.head('/doc.txt')
|
|
168
|
+
_(result).must_be_kind_of WebDAV::Response
|
|
169
|
+
end
|
|
170
|
+
end
|
|
171
|
+
end
|
|
172
|
+
|
|
173
|
+
describe "#post" do
|
|
174
|
+
it "returns a Response" do
|
|
175
|
+
dav.stub(:request, ok_response) do
|
|
176
|
+
result = dav.post('/endpoint', body: '<data/>')
|
|
177
|
+
_(result).must_be_kind_of WebDAV::Response
|
|
178
|
+
end
|
|
179
|
+
end
|
|
180
|
+
end
|
|
181
|
+
|
|
182
|
+
describe "#put" do
|
|
183
|
+
it "returns a Response" do
|
|
184
|
+
dav.stub(:request, created_response) do
|
|
185
|
+
result = dav.put('/doc.txt', body: 'New content', content_type: 'text/plain')
|
|
186
|
+
_(result).must_be_kind_of WebDAV::Response
|
|
187
|
+
_(result.code).must_equal 201
|
|
188
|
+
end
|
|
189
|
+
end
|
|
190
|
+
end
|
|
191
|
+
|
|
192
|
+
describe "#patch" do
|
|
193
|
+
it "returns a Response" do
|
|
194
|
+
dav.stub(:request, ok_response) do
|
|
195
|
+
result = dav.patch('/doc.txt', body: '<patch/>')
|
|
196
|
+
_(result).must_be_kind_of WebDAV::Response
|
|
197
|
+
end
|
|
198
|
+
end
|
|
199
|
+
end
|
|
200
|
+
|
|
201
|
+
describe "#delete" do
|
|
202
|
+
it "returns a Response" do
|
|
203
|
+
dav.stub(:request, no_content_response) do
|
|
204
|
+
result = dav.delete('/doc.txt')
|
|
205
|
+
_(result).must_be_kind_of WebDAV::Response
|
|
206
|
+
_(result.code).must_equal 204
|
|
207
|
+
end
|
|
208
|
+
end
|
|
209
|
+
end
|
|
210
|
+
|
|
211
|
+
describe "#options" do
|
|
212
|
+
it "returns a Response" do
|
|
213
|
+
dav.stub(:request, ok_response) do
|
|
214
|
+
result = dav.options('/')
|
|
215
|
+
_(result).must_be_kind_of WebDAV::Response
|
|
216
|
+
end
|
|
217
|
+
end
|
|
218
|
+
end
|
|
219
|
+
|
|
220
|
+
describe "#trace" do
|
|
221
|
+
it "returns a Response" do
|
|
222
|
+
dav.stub(:request, ok_response) do
|
|
223
|
+
result = dav.trace('/')
|
|
224
|
+
_(result).must_be_kind_of WebDAV::Response
|
|
225
|
+
end
|
|
226
|
+
end
|
|
227
|
+
end
|
|
228
|
+
|
|
229
|
+
# Error handling
|
|
230
|
+
|
|
231
|
+
describe "error responses" do
|
|
232
|
+
it "raises WebDAV::Error for 404" do
|
|
233
|
+
dav.stub(:request, not_found_response) do
|
|
234
|
+
assert_raises(WebDAV::Error) do
|
|
235
|
+
dav.get('/missing.txt')
|
|
236
|
+
end
|
|
237
|
+
end
|
|
238
|
+
end
|
|
239
|
+
|
|
240
|
+
it "includes the status code in the error" do
|
|
241
|
+
dav.stub(:request, not_found_response) do
|
|
242
|
+
error = assert_raises(WebDAV::Error){dav.get('/missing.txt')}
|
|
243
|
+
_(error.code).must_equal 404
|
|
244
|
+
end
|
|
245
|
+
end
|
|
246
|
+
|
|
247
|
+
it "includes the message in the error" do
|
|
248
|
+
dav.stub(:request, not_found_response) do
|
|
249
|
+
error = assert_raises(WebDAV::Error){dav.get('/missing.txt')}
|
|
250
|
+
_(error.message).must_equal 'Not Found'
|
|
251
|
+
end
|
|
252
|
+
end
|
|
253
|
+
end
|
|
254
|
+
end
|
data/webdav.gemspec
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
require_relative './lib/WebDAV/VERSION'
|
|
2
|
+
|
|
3
|
+
Gem::Specification.new do |spec|
|
|
4
|
+
spec.name = 'webdav'
|
|
5
|
+
spec.version = WebDAV::VERSION
|
|
6
|
+
|
|
7
|
+
spec.summary = "A Ruby WebDAV client library."
|
|
8
|
+
spec.description = "A Ruby WebDAV client library."
|
|
9
|
+
|
|
10
|
+
spec.author = 'thoran'
|
|
11
|
+
spec.email = 'code@thoran.com'
|
|
12
|
+
spec.homepage = 'https://github.com/thoran/webdav'
|
|
13
|
+
spec.license = 'MIT'
|
|
14
|
+
|
|
15
|
+
spec.required_ruby_version = '>= 2.7'
|
|
16
|
+
|
|
17
|
+
spec.add_dependency('http.rb')
|
|
18
|
+
|
|
19
|
+
spec.files = [
|
|
20
|
+
'webdav.gemspec',
|
|
21
|
+
'Gemfile',
|
|
22
|
+
Dir['lib/**/*.rb'],
|
|
23
|
+
'LICENSE',
|
|
24
|
+
'README.md',
|
|
25
|
+
Dir['test/**/*.rb']
|
|
26
|
+
].flatten
|
|
27
|
+
|
|
28
|
+
spec.require_paths = ['lib']
|
|
29
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: webdav
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.0.0
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- thoran
|
|
8
|
+
bindir: bin
|
|
9
|
+
cert_chain: []
|
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
11
|
+
dependencies:
|
|
12
|
+
- !ruby/object:Gem::Dependency
|
|
13
|
+
name: http.rb
|
|
14
|
+
requirement: !ruby/object:Gem::Requirement
|
|
15
|
+
requirements:
|
|
16
|
+
- - ">="
|
|
17
|
+
- !ruby/object:Gem::Version
|
|
18
|
+
version: '0'
|
|
19
|
+
type: :runtime
|
|
20
|
+
prerelease: false
|
|
21
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
22
|
+
requirements:
|
|
23
|
+
- - ">="
|
|
24
|
+
- !ruby/object:Gem::Version
|
|
25
|
+
version: '0'
|
|
26
|
+
description: A Ruby WebDAV client library.
|
|
27
|
+
email: code@thoran.com
|
|
28
|
+
executables: []
|
|
29
|
+
extensions: []
|
|
30
|
+
extra_rdoc_files: []
|
|
31
|
+
files:
|
|
32
|
+
- Gemfile
|
|
33
|
+
- LICENSE
|
|
34
|
+
- README.md
|
|
35
|
+
- lib/Net/HTTP/Report.rb
|
|
36
|
+
- lib/String/to_const.rb
|
|
37
|
+
- lib/Thoran/Array/AllButFirst/all_but_first.rb
|
|
38
|
+
- lib/Thoran/Array/FirstX/firstX.rb
|
|
39
|
+
- lib/Thoran/String/ToConst/to_const.rb
|
|
40
|
+
- lib/WebDAV/Error.rb
|
|
41
|
+
- lib/WebDAV/MultiStatus.rb
|
|
42
|
+
- lib/WebDAV/Response.rb
|
|
43
|
+
- lib/WebDAV/VERSION.rb
|
|
44
|
+
- lib/webdav.rb
|
|
45
|
+
- test/WebDAV/Error_test.rb
|
|
46
|
+
- test/WebDAV/MultiStatus_test.rb
|
|
47
|
+
- test/WebDAV/Response_test.rb
|
|
48
|
+
- test/helper.rb
|
|
49
|
+
- test/webdav_test.rb
|
|
50
|
+
- webdav.gemspec
|
|
51
|
+
homepage: https://github.com/thoran/webdav
|
|
52
|
+
licenses:
|
|
53
|
+
- MIT
|
|
54
|
+
metadata: {}
|
|
55
|
+
rdoc_options: []
|
|
56
|
+
require_paths:
|
|
57
|
+
- lib
|
|
58
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
59
|
+
requirements:
|
|
60
|
+
- - ">="
|
|
61
|
+
- !ruby/object:Gem::Version
|
|
62
|
+
version: '2.7'
|
|
63
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
64
|
+
requirements:
|
|
65
|
+
- - ">="
|
|
66
|
+
- !ruby/object:Gem::Version
|
|
67
|
+
version: '0'
|
|
68
|
+
requirements: []
|
|
69
|
+
rubygems_version: 4.0.8
|
|
70
|
+
specification_version: 4
|
|
71
|
+
summary: A Ruby WebDAV client library.
|
|
72
|
+
test_files: []
|